Merge "Use static mocking in `DesktopModeLaunchParamsModifierTests`." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index fa11278..a42adad 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -173,6 +173,11 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+cc_aconfig_library {
+    name: "com.android.window.flags.window-aconfig_flags_c_lib",
+    aconfig_declarations: "com.android.window.flags.window-aconfig",
+}
+
 // DeviceStateManager
 aconfig_declarations {
     name: "android.hardware.devicestate.feature.flags-aconfig",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index e649485..e82df12 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -41,10 +41,10 @@
             ]
         },
         {
-            "name": "CtsHostsideNetworkTests",
+            "name": "CtsHostsideNetworkPolicyTests",
             "options": [
-                {"include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests#testMeteredNetworkAccess_expeditedJob"},
-                {"include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests#testNonMeteredNetworkAccess_expeditedJob"}
+                {"include-filter": "com.android.cts.netpolicy.HostsideRestrictBackgroundNetworkTests#testMeteredNetworkAccess_expeditedJob"},
+                {"include-filter": "com.android.cts.netpolicy.HostsideRestrictBackgroundNetworkTests#testNonMeteredNetworkAccess_expeditedJob"}
             ]
         },
         {
diff --git a/core/api/current.txt b/core/api/current.txt
index 2f2a765..53cf7d5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -451,12 +451,12 @@
     field public static final int alertDialogTheme = 16843529; // 0x1010309
     field public static final int alignmentMode = 16843642; // 0x101037a
     field public static final int allContactsName = 16843468; // 0x10102cc
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int allow;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int allow = 16844430; // 0x101068e
     field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowClickWhenDisabled = 16844312; // 0x1010618
-    field @FlaggedApi("android.security.asm_restrictions_enabled") public static final int allowCrossUidActivitySwitchFromBelow;
+    field @FlaggedApi("android.security.asm_restrictions_enabled") public static final int allowCrossUidActivitySwitchFromBelow = 16844449; // 0x10106a1
     field public static final int allowEmbedded = 16843765; // 0x10103f5
     field public static final int allowGameAngleDriver = 16844376; // 0x1010658
     field public static final int allowGameDownscaling = 16844377; // 0x1010659
@@ -511,7 +511,7 @@
     field public static final int autoSizeTextType = 16844085; // 0x1010535
     field public static final int autoStart = 16843445; // 0x10102b5
     field @Deprecated public static final int autoText = 16843114; // 0x101016a
-    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int autoTransact;
+    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int autoTransact = 16844441; // 0x1010699
     field public static final int autoUrlDetect = 16843404; // 0x101028c
     field public static final int autoVerify = 16844014; // 0x10104ee
     field public static final int autofillHints = 16844118; // 0x1010556
@@ -658,7 +658,7 @@
     field public static final int contentInsetRight = 16843862; // 0x1010456
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
-    field @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public static final int contentSensitivity;
+    field @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public static final int contentSensitivity = 16844446; // 0x101069e
     field public static final int contextClickable = 16844007; // 0x10104e7
     field public static final int contextDescription = 16844078; // 0x101052e
     field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
@@ -688,7 +688,7 @@
     field public static final int debuggable = 16842767; // 0x101000f
     field public static final int defaultFocusHighlightEnabled = 16844130; // 0x1010562
     field public static final int defaultHeight = 16844021; // 0x10104f5
-    field @FlaggedApi("android.content.res.default_locale") public static final int defaultLocale;
+    field @FlaggedApi("android.content.res.default_locale") public static final int defaultLocale = 16844424; // 0x1010688
     field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504
     field public static final int defaultValue = 16843245; // 0x10101ed
     field public static final int defaultWidth = 16844020; // 0x10104f4
@@ -858,7 +858,7 @@
     field public static final int format24Hour = 16843723; // 0x10103cb
     field public static final int fraction = 16843992; // 0x10104d8
     field public static final int fragment = 16843491; // 0x10102e3
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentAdvancedPattern;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentAdvancedPattern = 16844438; // 0x1010696
     field public static final int fragmentAllowEnterTransitionOverlap = 16843976; // 0x10104c8
     field public static final int fragmentAllowReturnTransitionOverlap = 16843977; // 0x10104c9
     field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7
@@ -869,13 +869,13 @@
     field public static final int fragmentFadeExitAnimation = 16843498; // 0x10102ea
     field public static final int fragmentOpenEnterAnimation = 16843493; // 0x10102e5
     field public static final int fragmentOpenExitAnimation = 16843494; // 0x10102e6
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPattern;
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPrefix;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPattern = 16844437; // 0x1010695
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPrefix = 16844436; // 0x1010694
     field public static final int fragmentReenterTransition = 16843975; // 0x10104c7
     field public static final int fragmentReturnTransition = 16843973; // 0x10104c5
     field public static final int fragmentSharedElementEnterTransition = 16843972; // 0x10104c4
     field public static final int fragmentSharedElementReturnTransition = 16843974; // 0x10104c6
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentSuffix;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentSuffix = 16844439; // 0x1010697
     field public static final int freezesText = 16843116; // 0x101016c
     field public static final int fromAlpha = 16843210; // 0x10101ca
     field public static final int fromDegrees = 16843187; // 0x10101b3
@@ -1345,15 +1345,15 @@
     field public static final int propertyYName = 16843893; // 0x1010475
     field public static final int protectionLevel = 16842761; // 0x1010009
     field public static final int publicKey = 16843686; // 0x10103a6
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int query;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int query = 16844431; // 0x101068f
     field public static final int queryActionMsg = 16843227; // 0x10101db
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryAdvancedPattern;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryAdvancedPattern = 16844434; // 0x1010692
     field public static final int queryAfterZeroResults = 16843394; // 0x1010282
     field public static final int queryBackground = 16843911; // 0x1010487
     field public static final int queryHint = 16843608; // 0x1010358
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPattern;
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPrefix;
-    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int querySuffix;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPattern = 16844433; // 0x1010691
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPrefix = 16844432; // 0x1010690
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int querySuffix = 16844435; // 0x1010693
     field public static final int quickContactBadgeStyleSmallWindowLarge = 16843443; // 0x10102b3
     field public static final int quickContactBadgeStyleSmallWindowMedium = 16843442; // 0x10102b2
     field public static final int quickContactBadgeStyleSmallWindowSmall = 16843441; // 0x10102b1
@@ -1382,7 +1382,7 @@
     field public static final int reqTouchScreen = 16843303; // 0x1010227
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
     field public static final int requestRawExternalStorageAccess = 16844357; // 0x1010645
-    field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller;
+    field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller = 16844443; // 0x101069b
     field public static final int requireDeviceScreenOn = 16844317; // 0x101061d
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
@@ -1494,12 +1494,12 @@
     field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
     field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d
     field public static final int shell = 16844180; // 0x1010594
-    field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang;
+    field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang = 16844450; // 0x10106a2
     field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
     field public static final int shortcutId = 16844072; // 0x1010528
     field public static final int shortcutLongLabel = 16844074; // 0x101052a
     field public static final int shortcutShortLabel = 16844073; // 0x1010529
-    field @FlaggedApi("android.nfc.nfc_observe_mode") public static final int shouldDefaultToObserveMode;
+    field @FlaggedApi("android.nfc.nfc_observe_mode") public static final int shouldDefaultToObserveMode = 16844448; // 0x10106a0
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c
     field public static final int showAsAction = 16843481; // 0x10102d9
@@ -1610,7 +1610,7 @@
     field public static final int supportedTypes = 16844369; // 0x1010651
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
-    field @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public static final int supportsConnectionlessStylusHandwriting;
+    field @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public static final int supportsConnectionlessStylusHandwriting = 16844447; // 0x101069f
     field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
     field public static final int supportsInlineSuggestionsWithTouchExploration = 16844397; // 0x101066d
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
@@ -1631,7 +1631,7 @@
     field public static final int switchTextOff = 16843628; // 0x101036c
     field public static final int switchTextOn = 16843627; // 0x101036b
     field public static final int syncable = 16842777; // 0x1010019
-    field @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") public static final int systemUserOnly;
+    field @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") public static final int systemUserOnly = 16844429; // 0x101068d
     field public static final int tabStripEnabled = 16843453; // 0x10102bd
     field public static final int tabStripLeft = 16843451; // 0x10102bb
     field public static final int tabStripRight = 16843452; // 0x10102bc
@@ -1808,12 +1808,12 @@
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
     field public static final int use32bitAbi = 16844053; // 0x1010515
     field public static final int useAppZygote = 16844183; // 0x1010597
-    field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int useBoundsForWidth;
+    field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int useBoundsForWidth = 16844440; // 0x1010698
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useEmbeddedDex = 16844190; // 0x101059e
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
-    field @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public static final int useLocalePreferredLineHeightForMinimum;
+    field @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public static final int useLocalePreferredLineHeightForMinimum = 16844445; // 0x101069d
     field public static final int userVisible = 16843409; // 0x1010291
     field public static final int usesCleartextTraffic = 16844012; // 0x10104ec
     field public static final int usesPermissionFlags = 16844356; // 0x1010644
@@ -1892,7 +1892,7 @@
     field public static final int windowFullscreen = 16843277; // 0x101020d
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
-    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final int windowIsFrameRatePowerSavingsBalanced;
+    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final int windowIsFrameRatePowerSavingsBalanced = 16844451; // 0x10106a3
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
     field public static final int windowLayoutAffinity = 16844313; // 0x1010619
     field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
@@ -1903,7 +1903,7 @@
     field public static final int windowNoDisplay = 16843294; // 0x101021e
     field public static final int windowNoMoveAnimation = 16844421; // 0x1010685
     field public static final int windowNoTitle = 16842838; // 0x1010056
-    field @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") public static final int windowOptOutEdgeToEdgeEnforcement;
+    field @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") public static final int windowOptOutEdgeToEdgeEnforcement = 16844442; // 0x101069a
     field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf
     field public static final int windowReenterTransition = 16843951; // 0x10104af
     field public static final int windowReturnTransition = 16843950; // 0x10104ae
@@ -2015,19 +2015,19 @@
     field public static final int system_control_highlight_light = 17170558; // 0x106007e
     field public static final int system_control_normal_dark = 17170600; // 0x10600a8
     field public static final int system_control_normal_light = 17170557; // 0x106007d
-    field public static final int system_error_0;
-    field public static final int system_error_10;
-    field public static final int system_error_100;
-    field public static final int system_error_1000;
-    field public static final int system_error_200;
-    field public static final int system_error_300;
-    field public static final int system_error_400;
-    field public static final int system_error_50;
-    field public static final int system_error_500;
-    field public static final int system_error_600;
-    field public static final int system_error_700;
-    field public static final int system_error_800;
-    field public static final int system_error_900;
+    field public static final int system_error_0 = 17170629; // 0x10600c5
+    field public static final int system_error_10 = 17170630; // 0x10600c6
+    field public static final int system_error_100 = 17170632; // 0x10600c8
+    field public static final int system_error_1000 = 17170641; // 0x10600d1
+    field public static final int system_error_200 = 17170633; // 0x10600c9
+    field public static final int system_error_300 = 17170634; // 0x10600ca
+    field public static final int system_error_400 = 17170635; // 0x10600cb
+    field public static final int system_error_50 = 17170631; // 0x10600c7
+    field public static final int system_error_500 = 17170636; // 0x10600cc
+    field public static final int system_error_600 = 17170637; // 0x10600cd
+    field public static final int system_error_700 = 17170638; // 0x10600ce
+    field public static final int system_error_800 = 17170639; // 0x10600cf
+    field public static final int system_error_900 = 17170640; // 0x10600d0
     field public static final int system_error_container_dark = 17170597; // 0x10600a5
     field public static final int system_error_container_light = 17170554; // 0x106007a
     field public static final int system_error_dark = 17170595; // 0x10600a3
@@ -2077,7 +2077,7 @@
     field public static final int system_on_secondary_fixed_variant = 17170619; // 0x10600bb
     field public static final int system_on_secondary_light = 17170533; // 0x1060065
     field public static final int system_on_surface_dark = 17170584; // 0x1060098
-    field public static final int system_on_surface_disabled;
+    field public static final int system_on_surface_disabled = 17170627; // 0x10600c3
     field public static final int system_on_surface_light = 17170541; // 0x106006d
     field public static final int system_on_surface_variant_dark = 17170593; // 0x10600a1
     field public static final int system_on_surface_variant_light = 17170550; // 0x1060076
@@ -2088,7 +2088,7 @@
     field public static final int system_on_tertiary_fixed_variant = 17170623; // 0x10600bf
     field public static final int system_on_tertiary_light = 17170537; // 0x1060069
     field public static final int system_outline_dark = 17170594; // 0x10600a2
-    field public static final int system_outline_disabled;
+    field public static final int system_outline_disabled = 17170628; // 0x10600c4
     field public static final int system_outline_light = 17170551; // 0x1060077
     field public static final int system_outline_variant_dark = 17170625; // 0x10600c1
     field public static final int system_outline_variant_light = 17170624; // 0x10600c0
@@ -2129,7 +2129,7 @@
     field public static final int system_surface_dark = 17170583; // 0x1060097
     field public static final int system_surface_dim_dark = 17170591; // 0x106009f
     field public static final int system_surface_dim_light = 17170548; // 0x1060074
-    field public static final int system_surface_disabled;
+    field public static final int system_surface_disabled = 17170626; // 0x10600c2
     field public static final int system_surface_light = 17170540; // 0x106006c
     field public static final int system_surface_variant_dark = 17170592; // 0x10600a0
     field public static final int system_surface_variant_light = 17170549; // 0x1060075
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e12da63..f10c0fc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -437,10 +437,10 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
-    field @FlaggedApi("android.content.res.manifest_flagging") public static final int featureFlag;
+    field @FlaggedApi("android.content.res.manifest_flagging") public static final int featureFlag = 16844428; // 0x101068c
     field public static final int gameSessionService = 16844373; // 0x1010655
     field public static final int hotwordDetectionService = 16844326; // 0x1010626
-    field @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public static final int isVirtualDeviceOnly;
+    field @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public static final int isVirtualDeviceOnly = 16844425; // 0x1010689
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844305; // 0x1010611
     field public static final int playHomeTransitionSound = 16844358; // 0x1010646
@@ -492,9 +492,9 @@
     field public static final int config_defaultCallScreening = 17039398; // 0x1040026
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_defaultNotes = 17039429; // 0x1040045
-    field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo;
+    field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048
     field public static final int config_defaultSms = 17039396; // 0x1040024
-    field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet;
+    field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049
     field public static final int config_devicePolicyManagement = 17039421; // 0x104003d
     field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
     field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
diff --git a/core/java/android/app/AppOpInfo.java b/core/java/android/app/AppOpInfo.java
index 5268ec4..a0f0cca 100644
--- a/core/java/android/app/AppOpInfo.java
+++ b/core/java/android/app/AppOpInfo.java
@@ -88,7 +88,7 @@
 
     /**
      * This specifies whether each option is only allowed to be read
-     * by apps with manage appops permission.
+     * by apps with privileged appops permission.
      */
     public final boolean restrictRead;
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8e766c9..20b2357 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -3265,7 +3265,7 @@
     }
 
     /**
-     * Retrieve whether the op can be read by apps with manage appops permission.
+     * Retrieve whether the op can be read by apps with privileged appops permission.
      * @hide
      */
     public static boolean opRestrictsRead(int op) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f1e44cc..1df8f63 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1104,6 +1104,10 @@
             return true;
         }
 
+        if (mDataDir == null) {
+            return false;
+        }
+
         // Temporarily disable logging of disk reads on the Looper thread as this is necessary -
         // and the loader will access the directory anyway if we don't check it.
         StrictMode.ThreadPolicy oldThreadPolicy = allowThreadDiskReads();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4d7e29b..05a2aec 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9770,6 +9770,12 @@
      * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
      * <p>
      *
+     * <p>
+     * Starting at {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V} the
+     * {@link Notification#FLAG_NO_CLEAR NO_CLEAR flag} will be set for valid MediaStyle
+     * notifications.
+     * <p>
+     *
      * To use this style with your Notification, feed it to
      * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
      * <pre class="prettyprint">
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index f092945..9b3fb5c 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -1197,6 +1197,24 @@
     /**
      * Callback called when a particular foreground service type has timed out.
      *
+     * <p>This callback is meant to give the app a small grace period of a few seconds to finish
+     * the foreground service of the offending type - if it fails to do so, the app will be
+     * declared an ANR.
+     *
+     * <p>The foreground service of the offending type can be stopped within the time limit by
+     * {@link android.app.Service#stopSelf()},
+     * {@link android.content.Context#stopService(android.content.Intent)} or their overloads.
+     * {@link android.app.Service#stopForeground(int)} can be used as well, which demotes the
+     * service to a "background" service, which will soon be stopped by the system.
+     *
+     * <p>The specific time limit for each type (if one exists) is mentioned in the documentation
+     * for that foreground service type.
+     *
+     * <p>Note: time limits are restricted to a rolling 24-hour window - for example, if a
+     * foreground service type has a time limit of 6 hours, that time counter begins as soon as the
+     * foreground service starts. This time limit will only be reset once every 24 hours or if the
+     * app comes into the foreground state.
+     *
      * @param startId the startId passed to {@link #onStartCommand(Intent, int, int)} when
      *                the service started.
      * @param fgsType the {@link ServiceInfo.ForegroundServiceType foreground service type} which
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index c6a8762..342479b 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5062,21 +5062,29 @@
     /**
      * <p>The version of the session configuration query
      * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
-     * API</p>
+     * and {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics }
+     * APIs.</p>
      * <p>The possible values in this key correspond to the values defined in
      * android.os.Build.VERSION_CODES. Each version defines a set of feature combinations the
      * camera device must reliably report whether they are supported via
-     * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
-     * API. And the version is always less or equal to android.os.Build.VERSION.SDK_INT.</p>
-     * <p>If set to UPSIDE_DOWN_CAKE, this camera device doesn't support
      * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }.
-     * Calling the method for this camera ID throws an UnsupportedOperationException.</p>
-     * <p>If set to VANILLA_ICE_CREAM, the application can call
+     * It also defines the set of session specific keys in CameraCharacteristics when returned from
+     * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics }.
+     * The version is always less or equal to android.os.Build.VERSION.SDK_INT.</p>
+     * <p>If set to UPSIDE_DOWN_CAKE, this camera device doesn't support the
+     * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup } API.
+     * Trying to create a CameraDeviceSetup instance throws an UnsupportedOperationException.</p>
+     * <p>From VANILLA_ICE_CREAM onwards, the camera compliance tests verify a set of
+     * commonly used SessionConfigurations to ensure that the outputs of
      * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
-     * to check if the combinations of below features are supported.</p>
+     * and {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics }
+     * are accurate. The application is encouraged to use these SessionConfigurations when turning on
+     * multiple features at the same time.</p>
+     * <p>When set to VANILLA_ICE_CREAM, the combinations of the following configurations are verified
+     * by the compliance tests:</p>
      * <ul>
-     * <li>A subset of LIMITED-level device stream combinations.</li>
-     * </ul>
+     * <li>
+     * <p>A set of commonly used stream combinations:</p>
      * <table>
      * <thead>
      * <tr>
@@ -5084,257 +5092,108 @@
      * <th style="text-align: center;">Size</th>
      * <th style="text-align: center;">Target 2</th>
      * <th style="text-align: center;">Size</th>
-     * <th style="text-align: center;">Sample use case(s)</th>
      * </tr>
      * </thead>
      * <tbody>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;">Simple preview, GPU video processing, or no-preview video recording.</td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S1080P</td>
      * <td style="text-align: center;"></td>
      * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S720P</td>
      * <td style="text-align: center;"></td>
      * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;">In-application video/image processing.</td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;">Standard still imaging.</td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">MAXIMUM_16_9</td>
+     * </tr>
+     * <tr>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">UHD</td>
+     * </tr>
+     * <tr>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">S1440P</td>
+     * </tr>
+     * <tr>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">S1080P</td>
+     * </tr>
+     * <tr>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">S1080P</td>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">UHD</td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">MAXIMUM_16_9</td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;">In-app processing plus still capture.</td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">MAXIMUM</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;">JPEG</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;">Standard recording.</td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">UHD</td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
      * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">PREVIEW</td>
-     * <td style="text-align: center;">Preview plus in-app processing.</td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1440P</td>
-     * <td style="text-align: center;"></td>
-     * </tr>
-     * <tr>
-     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
      * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S1080P</td>
-     * <td style="text-align: center;"></td>
      * </tr>
      * <tr>
      * <td style="text-align: center;">PRIV</td>
-     * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;">YUV</td>
-     * <td style="text-align: center;">S720P</td>
-     * <td style="text-align: center;"></td>
+     * <td style="text-align: center;">XVGA</td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">MAXIMUM_4_3</td>
+     * </tr>
+     * <tr>
+     * <td style="text-align: center;">PRIV</td>
+     * <td style="text-align: center;">S1080P_4_3</td>
+     * <td style="text-align: center;">JPEG/JPEG_R</td>
+     * <td style="text-align: center;">MAXIMUM_4_3</td>
      * </tr>
      * </tbody>
      * </table>
-     * <pre><code>- {@code MAXIMUM} size refers to the camera device's maximum output resolution for
-     *   that format from {@code StreamConfigurationMap#getOutputSizes}. {@code PREVIEW} size
-     *   refers to the best size match to the device's screen resolution, or to 1080p
-     *   (@code 1920x1080}, whichever is smaller. Both sizes are guaranteed to be supported.
-     *
-     * - {@code S1440P} refers to {@code 1920x1440 (4:3)} and {@code 2560x1440 (16:9)}.
-     *   {@code S1080P} refers to {@code 1440x1080 (4:3)} and {@code 1920x1080 (16:9)}.
-     *   And {@code S720P} refers to {@code 960x720 (4:3)} and {@code 1280x720 (16:9)}.
-     *
-     * - If a combination contains a S1440P, S1080P, or S720P stream,
-     *   both 4:3 and 16:9 aspect ratio sizes can be queried. For example, for the
-     *   stream combination of {PRIV, S1440P, JPEG, MAXIMUM}, and if MAXIMUM ==
-     *   4032 x 3024, the application will be able to query both
-     *   {PRIV, 1920 x 1440, JPEG, 4032 x 3024} and {PRIV, 2560 x 1440, JPEG, 4032 x 2268}
-     *   without an exception being thrown.
-     * </code></pre>
      * <ul>
-     * <li>VIDEO_STABILIZATION_MODES: {OFF, PREVIEW}</li>
-     * <li>AE_TARGET_FPS_RANGE: { {<em>, 30}, {</em>, 60} }</li>
-     * <li>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</li>
+     * <li>{@code MAXIMUM_4_3} refers to the camera device's maximum output resolution with
+     *   4:3 aspect ratio for that format from {@code StreamConfigurationMap#getOutputSizes}.</li>
+     * <li>{@code MAXIMUM_16_9} is the maximum output resolution with 16:9 aspect ratio.</li>
+     * <li>{@code S1440P} refers to {@code 2560x1440 (16:9)}.</li>
+     * <li>{@code S1080P} refers to {@code 1920x1080 (16:9)}.</li>
+     * <li>{@code S720P} refers to {@code 1280x720 (16:9)}.</li>
+     * <li>{@code UHD} refers to {@code 3840x2160 (16:9)}.</li>
+     * <li>{@code XVGA} refers to {@code 1024x768 (4:3)}.</li>
+     * <li>{@code S1080P_43} refers to {@code 1440x1080 (4:3)}.</li>
      * </ul>
+     * </li>
+     * <li>
+     * <p>VIDEO_STABILIZATION_MODE: {OFF, PREVIEW}</p>
+     * </li>
+     * <li>
+     * <p>AE_TARGET_FPS_RANGE: { {*, 30}, {*, 60} }</p>
+     * </li>
+     * <li>
+     * <p>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</p>
+     * </li>
+     * </ul>
+     * <p>All of the above configurations can be set up with a SessionConfiguration. The list of
+     * OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and
+     * the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p>
      * <p>This key is available on all devices.</p>
      */
     @PublicKey
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index eb644e8..dfbf06b 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -917,8 +917,11 @@
      * image. For example, it can be used as a temporary placeholder for the requested capture
      * while the final image is being processed. The supported sizes for a still capture's postview
      * can be retrieved using
-     * {@link CameraExtensionCharacteristics#getPostviewSupportedSizes(int, Size, int)}.
-     * The formats of the still capture and postview should be equivalent upon capture request.</p>
+     * {@link CameraExtensionCharacteristics#getPostviewSupportedSizes(int, Size, int)}.</p>
+     *
+     * <p>Starting with Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
+     * the formats of the still capture and postview are not required to be equivalent upon capture
+     * request.</p>
      *
      * @param extension the extension type
      * @return {@code true} in case postview is supported, {@code false} otherwise
@@ -976,8 +979,7 @@
      *
      * @param extension the extension type
      * @param captureSize size of the still capture for which the postview is requested
-     * @param format device-specific extension output format of the still capture and
-     * postview
+     * @param format device-specific extension output format of the postview
      * @return non-modifiable list of available sizes or an empty list if the format and
      * size is not supported.
      * @throws IllegalArgumentException in case of unsupported extension or if postview
@@ -1018,8 +1020,8 @@
                 }
                 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
                 extender.init(mCameraId, mCharacteristicsMapNative);
-                return generateSupportedSizes(extender.getSupportedPostviewResolutions(
-                    sz), format, streamMap);
+                return getSupportedSizes(extender.getSupportedPostviewResolutions(sz),
+                        format);
             } else {
                 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
                         initializeExtension(extension);
@@ -1034,15 +1036,13 @@
                 }
 
                 if (format == ImageFormat.YUV_420_888) {
-                    return generateSupportedSizes(
-                            extenders.second.getSupportedPostviewResolutions(sz),
-                            format, streamMap);
+                    return getSupportedSizes(
+                            extenders.second.getSupportedPostviewResolutions(sz), format);
                 } else if (format == ImageFormat.JPEG) {
                     // The framework will perform the additional encoding pass on the
                     // processed YUV_420 buffers.
-                    return generateJpegSupportedSizes(
-                            extenders.second.getSupportedPostviewResolutions(sz),
-                                    streamMap);
+                    return getSupportedSizes(
+                            extenders.second.getSupportedPostviewResolutions(sz), format);
                 }  else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) {
                     // Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the basic
                     // extension case
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 875550a..a10e250 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -36,6 +36,8 @@
 import android.util.Log;
 import android.view.Surface;
 
+import com.android.internal.camera.flags.Flags;
+
 import java.nio.ByteBuffer;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -57,6 +59,8 @@
     private android.hardware.camera2.extension.Size mResolution = null;
     private android.hardware.camera2.extension.Size mPostviewResolution = null;
     private int mFormat = -1;
+    private int mPostviewFormat = -1;
+    private int mCaptureFormat = -1;
     private Surface mOutputSurface = null;
     private ImageWriter mOutputWriter = null;
     private Surface mPostviewOutputSurface = null;
@@ -204,10 +208,12 @@
     }
 
     public void onOutputSurface(Surface surface, int format) throws RemoteException {
-        if (format != ImageFormat.JPEG) {
+        if (!Flags.extension10Bit() && format != ImageFormat.JPEG) {
             Log.e(TAG, "Unsupported output format: " + format);
             return;
         }
+        CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(surface);
+        mCaptureFormat = surfaceInfo.mFormat;
         mOutputSurface = surface;
         initializePipeline();
     }
@@ -215,10 +221,11 @@
     public void onPostviewOutputSurface(Surface surface) throws RemoteException {
         CameraExtensionUtils.SurfaceInfo postviewSurfaceInfo =
                 CameraExtensionUtils.querySurface(surface);
-        if (postviewSurfaceInfo.mFormat != ImageFormat.JPEG) {
+        if (!Flags.extension10Bit() && postviewSurfaceInfo.mFormat != ImageFormat.JPEG) {
             Log.e(TAG, "Unsupported output format: " + postviewSurfaceInfo.mFormat);
             return;
         }
+        mPostviewFormat = postviewSurfaceInfo.mFormat;
         mPostviewOutputSurface = surface;
         initializePostviewPipeline();
     }
@@ -233,7 +240,7 @@
     }
 
     public void onImageFormatUpdate(int format) throws RemoteException {
-        if (format != ImageFormat.YUV_420_888) {
+        if (!Flags.extension10Bit() && format != ImageFormat.YUV_420_888) {
             Log.e(TAG, "Unsupported input format: " + format);
             return;
         }
@@ -244,33 +251,45 @@
     private void initializePipeline() throws RemoteException {
         if ((mFormat != -1) && (mOutputSurface != null) && (mResolution != null) &&
                 (mYuvReader == null)) {
-            // Jpeg/blobs are expected to be configured with (w*h)x1.5 + 64k Jpeg APP1 segment
-            mOutputWriter = ImageWriter.newInstance(mOutputSurface, 1 /*maxImages*/,
-                    ImageFormat.JPEG,
-                    (mResolution.width * mResolution.height * 3)/2 + JPEG_APP_SEGMENT_SIZE, 1);
-            mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height, mFormat,
-                    JPEG_QUEUE_SIZE);
-            mYuvReader.setOnImageAvailableListener(
-                    new YuvCallback(mYuvReader, mOutputWriter), mHandler);
-            mProcessor.onOutputSurface(mYuvReader.getSurface(), mFormat);
+            if (Flags.extension10Bit() && mCaptureFormat == ImageFormat.YUV_420_888) {
+                // For the case when postview is JPEG and capture is YUV
+                mProcessor.onOutputSurface(mOutputSurface, mCaptureFormat);
+            } else {
+                // Jpeg/blobs are expected to be configured with (w*h)x1.5 + 64k Jpeg APP1 segment
+                mOutputWriter = ImageWriter.newInstance(mOutputSurface, 1 /*maxImages*/,
+                        ImageFormat.JPEG,
+                        (mResolution.width * mResolution.height * 3) / 2
+                        + JPEG_APP_SEGMENT_SIZE, 1);
+                mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height,
+                        mFormat, JPEG_QUEUE_SIZE);
+                mYuvReader.setOnImageAvailableListener(
+                        new YuvCallback(mYuvReader, mOutputWriter), mHandler);
+                mProcessor.onOutputSurface(mYuvReader.getSurface(), mFormat);
+            }
             mProcessor.onResolutionUpdate(mResolution, mPostviewResolution);
-            mProcessor.onImageFormatUpdate(mFormat);
+            mProcessor.onImageFormatUpdate(ImageFormat.YUV_420_888);
         }
     }
 
     private void initializePostviewPipeline() throws RemoteException {
         if ((mFormat != -1) && (mPostviewOutputSurface != null) && (mPostviewResolution != null)
                 && (mPostviewYuvReader == null)) {
-            // Jpeg/blobs are expected to be configured with (w*h)x1
-            mPostviewOutputWriter = ImageWriter.newInstance(mPostviewOutputSurface, 1/*maxImages*/,
-                    ImageFormat.JPEG, mPostviewResolution.width * mPostviewResolution.height, 1);
-            mPostviewYuvReader = ImageReader.newInstance(mPostviewResolution.width,
-                    mPostviewResolution.height, mFormat, JPEG_QUEUE_SIZE);
-            mPostviewYuvReader.setOnImageAvailableListener(
-                    new YuvCallback(mPostviewYuvReader, mPostviewOutputWriter), mHandler);
-            mProcessor.onPostviewOutputSurface(mPostviewYuvReader.getSurface());
+            if (Flags.extension10Bit() && mPostviewFormat == ImageFormat.YUV_420_888) {
+                // For the case when postview is YUV and capture is JPEG
+                mProcessor.onPostviewOutputSurface(mPostviewOutputSurface);
+            } else {
+                // Jpeg/blobs are expected to be configured with (w*h)x1
+                mPostviewOutputWriter = ImageWriter.newInstance(mPostviewOutputSurface,
+                        1/*maxImages*/, ImageFormat.JPEG,
+                        mPostviewResolution.width * mPostviewResolution.height, 1);
+                mPostviewYuvReader = ImageReader.newInstance(mPostviewResolution.width,
+                        mPostviewResolution.height, mFormat, JPEG_QUEUE_SIZE);
+                mPostviewYuvReader.setOnImageAvailableListener(
+                        new YuvCallback(mPostviewYuvReader, mPostviewOutputWriter), mHandler);
+                mProcessor.onPostviewOutputSurface(mPostviewYuvReader.getSurface());
+            }
             mProcessor.onResolutionUpdate(mResolution, mPostviewResolution);
-            mProcessor.onImageFormatUpdate(mFormat);
+            mProcessor.onImageFormatUpdate(ImageFormat.YUV_420_888);
         }
     }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index c00e610..3ae3199 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -390,7 +390,16 @@
                 if (surfaceInfo.mFormat == ImageFormat.JPEG) {
                     mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
                     mImageProcessor = mImageJpegProcessor;
+                } else if (Flags.extension10Bit() && mClientPostviewSurface != null) {
+                    // Handles case when postview is JPEG and capture is YUV
+                    CameraExtensionUtils.SurfaceInfo postviewSurfaceInfo =
+                            CameraExtensionUtils.querySurface(mClientPostviewSurface);
+                    if (postviewSurfaceInfo.mFormat == ImageFormat.JPEG) {
+                        mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
+                        mImageProcessor = mImageJpegProcessor;
+                    }
                 }
+
                 mBurstCaptureImageReader = ImageReader.newInstance(surfaceInfo.mWidth,
                         surfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
                         mImageExtender.getMaxCaptureStage());
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
index f0c6e2e..40f0477 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -112,19 +112,30 @@
         if (outputConfig == null) return null;
 
         SurfaceInfo surfaceInfo = querySurface(outputConfig.getSurface());
-        if (surfaceInfo.mFormat == captureFormat) {
-            if (supportedPostviewSizes.containsKey(captureFormat)) {
-                Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
-                if (supportedPostviewSizes.get(surfaceInfo.mFormat)
-                        .contains(postviewSize)) {
-                    return outputConfig.getSurface();
-                } else {
-                    throw new IllegalArgumentException("Postview size not supported!");
-                }
+
+        if (Flags.extension10Bit()) {
+            Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
+            if (supportedPostviewSizes.get(surfaceInfo.mFormat)
+                    .contains(postviewSize)) {
+                return outputConfig.getSurface();
+            } else {
+                throw new IllegalArgumentException("Postview size not supported!");
             }
         } else {
-            throw new IllegalArgumentException("Postview format should be equivalent to " +
-                    " the capture format!");
+            if (surfaceInfo.mFormat == captureFormat) {
+                if (supportedPostviewSizes.containsKey(captureFormat)) {
+                    Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
+                    if (supportedPostviewSizes.get(surfaceInfo.mFormat)
+                            .contains(postviewSize)) {
+                        return outputConfig.getSurface();
+                    } else {
+                        throw new IllegalArgumentException("Postview size not supported!");
+                    }
+                }
+            } else {
+                throw new IllegalArgumentException("Postview format should be equivalent to "
+                        + " the capture format!");
+            }
         }
 
         return null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 72ab970..e6ddf35 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1736,6 +1736,24 @@
             "android.settings.NETWORK_OPERATOR_SETTINGS";
 
     /**
+     * Activity Action: Show settings for selecting the network provider.
+     * <p>
+     * In some cases, a matching Activity may not be provided, so ensure you
+     * safeguard against this.
+     * <p>
+     * Access to this preference can be customized via Settings' app.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+            "android.settings.NETWORK_PROVIDER_SETTINGS";
+
+    /**
      * Activity Action: Show settings for selection of 2G/3G.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 997c9581..5f6bdbf 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1383,16 +1383,22 @@
                 DreamService.DREAM_META_DATA, DREAM_META_DATA_ROOT_TAG,
                 com.android.internal.R.styleable.Dream)) {
             if (rawMetadata == null) return null;
-            return new DreamMetadata(
-                    convertToComponentName(
-                    rawMetadata.getString(
-                            com.android.internal.R.styleable.Dream_settingsActivity), serviceInfo),
-                    rawMetadata.getDrawable(
-                            com.android.internal.R.styleable.Dream_previewImage),
-                    rawMetadata.getBoolean(R.styleable.Dream_showClockAndComplications,
-                            DEFAULT_SHOW_COMPLICATIONS),
-                    rawMetadata.getInt(R.styleable.Dream_dreamCategory, DREAM_CATEGORY_DEFAULT)
-                    );
+            try {
+                return new DreamMetadata(
+                        convertToComponentName(
+                                rawMetadata.getString(
+                                        com.android.internal.R.styleable.Dream_settingsActivity),
+                                serviceInfo),
+                        rawMetadata.getDrawable(
+                                com.android.internal.R.styleable.Dream_previewImage),
+                        rawMetadata.getBoolean(R.styleable.Dream_showClockAndComplications,
+                                DEFAULT_SHOW_COMPLICATIONS),
+                        rawMetadata.getInt(R.styleable.Dream_dreamCategory, DREAM_CATEGORY_DEFAULT)
+                );
+            } catch (Exception exception) {
+                Log.e(TAG, "Failed to create read metadata", exception);
+                return null;
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3bc6829..f0d27da 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4276,6 +4276,10 @@
             mPreferredFrameRate = -1;
             mIsFrameRateConflicted = false;
             mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
+        } else if (mPreferredFrameRate == 0) {
+            // From MSG_FRAME_RATE_SETTING, where mPreferredFrameRate is set to 0
+            setPreferredFrameRate(0);
+            mPreferredFrameRate = -1;
         }
     }
 
@@ -12565,6 +12569,13 @@
             case FRAME_RATE_CATEGORY_HIGH ->
                     mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
         }
+
+        // If it's currently an intermittent update,
+        // we should keep mPreferredFrameRateCategory as NORMAL
+        if (intermittentUpdateState() == INTERMITTENT_STATE_INTERMITTENT) {
+            return;
+        }
+
         if (mFrameRateCategoryHighCount > 0) {
             mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
         } else if (mFrameRateCategoryHighHintCount > 0) {
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 7c7c7b8..9f9aae5 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -473,7 +473,14 @@
         } finally {
             // Unconditionally skip to the end of the written data, even if the actual parcel
             // format is incompatible
-            parcel.setDataPosition(endPos);
+            if (endPos > parcel.dataPosition()) {
+                if (endPos >= parcel.dataSize()) {
+                    throw new IndexOutOfBoundsException(
+                            "PowerStats end position: " + endPos + " is outside the parcel bounds: "
+                                    + parcel.dataSize());
+                }
+                parcel.setDataPosition(endPos);
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 69d3d6a..c21a43e 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -53,8 +53,6 @@
 
     public ScreenshotHelper(Context context) {
         mContext = context;
-        IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
     }
 
     /**
@@ -108,6 +106,8 @@
     public void takeScreenshotInternal(ScreenshotRequest request, @NonNull Handler handler,
             @Nullable Consumer<Uri> completionConsumer, long timeoutMs) {
         synchronized (mScreenshotLock) {
+            mContext.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(ACTION_USER_SWITCHED), Context.RECEIVER_EXPORTED);
 
             final Runnable mScreenshotTimeout = () -> {
                 synchronized (mScreenshotLock) {
@@ -223,6 +223,11 @@
             mScreenshotConnection = null;
             mScreenshotService = null;
         }
+        try {
+            mContext.unregisterReceiver(mBroadcastReceiver);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "Attempted to remove broadcast receiver twice");
+        }
     }
 
     /**
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 91ef324..0d1be38 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3656,6 +3656,7 @@
          "emergency" = Launch emergency dialer
          "lockdown" = Lock down device until the user authenticates
          "logout" =  Logout the current user
+         "system_update" = Launch System Update screen
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>emergency</item>
diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml
index daa0f553..d421944 100644
--- a/core/res/res/values/public-final.xml
+++ b/core/res/res/values/public-final.xml
@@ -3741,4 +3741,185 @@
     <!-- @hide @SystemApi -->
   <public type="bool" name="config_enableDefaultNotesForWorkProfile" id="0x0111000b" />
 
+  <!-- ===============================================================
+    Resources added in version NEXT of the platform
+
+    NOTE: After this version of the platform is forked, changes cannot be made to the root
+    branch's groups for that release. Only merge changes to the forked platform branch.
+    =============================================================== -->
+  <eat-comment/>
+
+  <staging-public-group-final type="attr" first-id="0x01bd0000">
+    <!-- @FlaggedApi("android.content.res.default_locale") -->
+    <public name="defaultLocale"/>
+    <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime")
+         @hide @SystemApi -->
+    <public name="isVirtualDeviceOnly"/>
+    <!-- Marking this entry as removed since it's not being finalized -->
+    <public name="removed_optional" />
+    <!-- Marking this entry as removed since it's not being finalized -->
+    <public name="removed_adServiceTypes" />
+    <!-- @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") -->
+    <public name="featureFlag"/>
+    <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") -->
+    <public name="systemUserOnly"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="allow"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="query"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="queryPrefix"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="queryPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="queryAdvancedPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="querySuffix"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentPrefix"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentAdvancedPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentSuffix"/>
+    <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+    <public name="useBoundsForWidth"/>
+    <!-- @FlaggedApi("android.nfc.nfc_read_polling_loop") -->
+    <public name="autoTransact"/>
+    <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") -->
+    <public name="windowOptOutEdgeToEdgeEnforcement"/>
+    <!-- @FlaggedApi("android.security.content_uri_permission_apis") -->
+    <public name="requireContentUriPermissionFromCaller" />
+    <!-- Marking this entry as removed since it's not being finalized -->
+    <public name="removed_languageSettingsActivity" />
+    <!-- @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") -->
+    <public name="useLocalePreferredLineHeightForMinimum"/>
+    <!-- @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") -->
+    <public name="contentSensitivity" />
+    <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") -->
+    <public name="supportsConnectionlessStylusHandwriting" />
+    <!-- @FlaggedApi("android.nfc.nfc_observe_mode") -->
+    <public name="shouldDefaultToObserveMode"/>
+    <!-- @FlaggedApi("android.security.asm_restrictions_enabled") -->
+    <public name="allowCrossUidActivitySwitchFromBelow"/>
+    <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+    <public name="shiftDrawingOffsetForStartOverhang" />
+    <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") -->
+    <public name="windowIsFrameRatePowerSavingsBalanced"/>
+    <!-- Marking this entry as removed since it's not being finalized -->
+    <public name="removed_dreamCategory" />
+  </staging-public-group-final>
+
+    <!-- @FlaggedApi("android.content.res.default_locale") -->
+  <public type="attr" name="defaultLocale" id="0x01010688" />
+    <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime")
+         @hide @SystemApi -->
+  <public type="attr" name="isVirtualDeviceOnly" id="0x01010689" />
+    <!-- @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") -->
+  <public type="attr" name="featureFlag" id="0x0101068c" />
+    <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") -->
+  <public type="attr" name="systemUserOnly" id="0x0101068d" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="allow" id="0x0101068e" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="query" id="0x0101068f" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="queryPrefix" id="0x01010690" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="queryPattern" id="0x01010691" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="queryAdvancedPattern" id="0x01010692" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="querySuffix" id="0x01010693" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="fragmentPrefix" id="0x01010694" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="fragmentPattern" id="0x01010695" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="fragmentAdvancedPattern" id="0x01010696" />
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+  <public type="attr" name="fragmentSuffix" id="0x01010697" />
+    <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+  <public type="attr" name="useBoundsForWidth" id="0x01010698" />
+    <!-- @FlaggedApi("android.nfc.nfc_read_polling_loop") -->
+  <public type="attr" name="autoTransact" id="0x01010699" />
+    <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") -->
+  <public type="attr" name="windowOptOutEdgeToEdgeEnforcement" id="0x0101069a" />
+    <!-- @FlaggedApi("android.security.content_uri_permission_apis") -->
+  <public type="attr" name="requireContentUriPermissionFromCaller" id="0x0101069b" />
+    <!-- @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") -->
+  <public type="attr" name="useLocalePreferredLineHeightForMinimum" id="0x0101069d" />
+    <!-- @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") -->
+  <public type="attr" name="contentSensitivity" id="0x0101069e" />
+    <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") -->
+  <public type="attr" name="supportsConnectionlessStylusHandwriting" id="0x0101069f" />
+    <!-- @FlaggedApi("android.nfc.nfc_observe_mode") -->
+  <public type="attr" name="shouldDefaultToObserveMode" id="0x010106a0" />
+    <!-- @FlaggedApi("android.security.asm_restrictions_enabled") -->
+  <public type="attr" name="allowCrossUidActivitySwitchFromBelow" id="0x010106a1" />
+    <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+  <public type="attr" name="shiftDrawingOffsetForStartOverhang" id="0x010106a2" />
+    <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") -->
+  <public type="attr" name="windowIsFrameRatePowerSavingsBalanced" id="0x010106a3" />
+
+  <staging-public-group-final type="string" first-id="0x01ba0000">
+    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") -->
+    <public name="config_defaultRetailDemo" />
+    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") -->
+    <public name="config_defaultWallet" />
+  </staging-public-group-final>
+
+    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") -->
+  <public type="string" name="config_defaultRetailDemo" id="0x01040048" />
+    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") -->
+  <public type="string" name="config_defaultWallet" id="0x01040049" />
+
+  <staging-public-group-final type="dimen" first-id="0x01b90000">
+    <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
+    <public name="removed_system_corner_radius_xsmall" />
+    <public name="removed_system_corner_radius_small" />
+    <public name="removed_system_corner_radius_medium" />
+    <public name="removed_system_corner_radius_large" />
+    <public name="removed_system_corner_radius_xlarge" />
+  </staging-public-group-final>
+
+    <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
+
+  <staging-public-group-final type="color" first-id="0x01b80000">
+    <public name="system_surface_disabled"/>
+    <public name="system_on_surface_disabled"/>
+    <public name="system_outline_disabled"/>
+    <public name="system_error_0"/>
+    <public name="system_error_10"/>
+    <public name="system_error_50"/>
+    <public name="system_error_100"/>
+    <public name="system_error_200"/>
+    <public name="system_error_300"/>
+    <public name="system_error_400"/>
+    <public name="system_error_500"/>
+    <public name="system_error_600"/>
+    <public name="system_error_700"/>
+    <public name="system_error_800"/>
+    <public name="system_error_900"/>
+    <public name="system_error_1000"/>
+  </staging-public-group-final>
+
+  <public type="color" name="system_surface_disabled" id="0x010600c2" />
+  <public type="color" name="system_on_surface_disabled" id="0x010600c3" />
+  <public type="color" name="system_outline_disabled" id="0x010600c4" />
+  <public type="color" name="system_error_0" id="0x010600c5" />
+  <public type="color" name="system_error_10" id="0x010600c6" />
+  <public type="color" name="system_error_50" id="0x010600c7" />
+  <public type="color" name="system_error_100" id="0x010600c8" />
+  <public type="color" name="system_error_200" id="0x010600c9" />
+  <public type="color" name="system_error_300" id="0x010600ca" />
+  <public type="color" name="system_error_400" id="0x010600cb" />
+  <public type="color" name="system_error_500" id="0x010600cc" />
+  <public type="color" name="system_error_600" id="0x010600cd" />
+  <public type="color" name="system_error_700" id="0x010600ce" />
+  <public type="color" name="system_error_800" id="0x010600cf" />
+  <public type="color" name="system_error_900" id="0x010600d0" />
+  <public type="color" name="system_error_1000" id="0x010600d1" />
+
 </resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index c84f781..b64334f 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -109,143 +109,66 @@
     =============================================================== -->
   <eat-comment/>
 
-  <staging-public-group type="attr" first-id="0x01bd0000">
-    <!-- @FlaggedApi("android.content.res.default_locale") -->
-    <public name="defaultLocale"/>
-    <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime")
-         @hide @SystemApi -->
-    <public name="isVirtualDeviceOnly"/>
-    <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") -->
+  <staging-public-group type="attr" first-id="0x01b70000">
+      <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") -->
     <public name="optional"/>
     <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") -->
     <public name="adServiceTypes" />
-    <!-- @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") -->
-    <public name="featureFlag"/>
-    <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") -->
-    <public name="systemUserOnly"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="allow"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="query"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="queryPrefix"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="queryPattern"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="queryAdvancedPattern"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="querySuffix"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="fragmentPrefix"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="fragmentPattern"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="fragmentAdvancedPattern"/>
-    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
-    <public name="fragmentSuffix"/>
-    <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
-    <public name="useBoundsForWidth"/>
-    <!-- @FlaggedApi("android.nfc.nfc_read_polling_loop") -->
-    <public name="autoTransact"/>
-    <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") -->
-    <public name="windowOptOutEdgeToEdgeEnforcement"/>
-    <!-- @FlaggedApi("android.security.content_uri_permission_apis") -->
-    <public name="requireContentUriPermissionFromCaller" />
     <!-- @FlaggedApi("android.view.inputmethod.ime_switcher_revamp") -->
     <public name="languageSettingsActivity"/>
-    <!-- @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") -->
-    <public name="useLocalePreferredLineHeightForMinimum"/>
-    <!-- @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") -->
-    <public name="contentSensitivity" />
-    <!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") -->
-    <public name="supportsConnectionlessStylusHandwriting" />
-    <!-- @FlaggedApi("android.nfc.nfc_observe_mode") -->
-    <public name="shouldDefaultToObserveMode"/>
-    <!-- @FlaggedApi("android.security.asm_restrictions_enabled") -->
-    <public name="allowCrossUidActivitySwitchFromBelow"/>
-    <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
-    <public name="shiftDrawingOffsetForStartOverhang" />
-    <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") -->
-    <public name="windowIsFrameRatePowerSavingsBalanced"/>
     <!-- @FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM") -->
     <public name="dreamCategory"/>
   </staging-public-group>
 
-  <staging-public-group type="id" first-id="0x01bc0000">
+  <staging-public-group type="id" first-id="0x01b60000">
   </staging-public-group>
 
-  <staging-public-group type="style" first-id="0x01bb0000">
+  <staging-public-group type="style" first-id="0x01b50000">
   </staging-public-group>
 
-  <staging-public-group type="string" first-id="0x01ba0000">
-    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") -->
-    <public name="config_defaultRetailDemo" />
-    <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.wallet_role_enabled") -->
-    <public name="config_defaultWallet" />
+  <staging-public-group type="string" first-id="0x01b40000">
   </staging-public-group>
 
-  <staging-public-group type="dimen" first-id="0x01b90000">
-    <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
-    <public name="removed_system_corner_radius_xsmall" />
-    <public name="removed_system_corner_radius_small" />
-    <public name="removed_system_corner_radius_medium" />
-    <public name="removed_system_corner_radius_large" />
-    <public name="removed_system_corner_radius_xlarge" />
+  <staging-public-group type="dimen" first-id="0x01b30000">
   </staging-public-group>
 
-  <staging-public-group type="color" first-id="0x01b80000">
-    <public name="system_surface_disabled"/>
-    <public name="system_on_surface_disabled"/>
-    <public name="system_outline_disabled"/>
-    <public name="system_error_0"/>
-    <public name="system_error_10"/>
-    <public name="system_error_50"/>
-    <public name="system_error_100"/>
-    <public name="system_error_200"/>
-    <public name="system_error_300"/>
-    <public name="system_error_400"/>
-    <public name="system_error_500"/>
-    <public name="system_error_600"/>
-    <public name="system_error_700"/>
-    <public name="system_error_800"/>
-    <public name="system_error_900"/>
-    <public name="system_error_1000"/>
+  <staging-public-group type="color" first-id="0x01b20000">
   </staging-public-group>
 
-  <staging-public-group type="array" first-id="0x01b70000">
+  <staging-public-group type="array" first-id="0x01b10000">
   </staging-public-group>
 
-  <staging-public-group type="drawable" first-id="0x01b60000">
+  <staging-public-group type="drawable" first-id="0x01b00000">
   </staging-public-group>
 
-  <staging-public-group type="layout" first-id="0x01b50000">
+  <staging-public-group type="layout" first-id="0x01af0000">
   </staging-public-group>
 
-  <staging-public-group type="anim" first-id="0x01b40000">
+  <staging-public-group type="anim" first-id="0x01ae0000">
   </staging-public-group>
 
-  <staging-public-group type="animator" first-id="0x01b30000">
+  <staging-public-group type="animator" first-id="0x01ad0000">
   </staging-public-group>
 
-  <staging-public-group type="interpolator" first-id="0x01b20000">
+  <staging-public-group type="interpolator" first-id="0x01ac0000">
   </staging-public-group>
 
-  <staging-public-group type="mipmap" first-id="0x01b10000">
+  <staging-public-group type="mipmap" first-id="0x01ab0000">
   </staging-public-group>
 
-  <staging-public-group type="integer" first-id="0x01b00000">
+  <staging-public-group type="integer" first-id="0x01aa0000">
   </staging-public-group>
 
-  <staging-public-group type="transition" first-id="0x01af0000">
+  <staging-public-group type="transition" first-id="0x01a90000">
   </staging-public-group>
 
-  <staging-public-group type="raw" first-id="0x01ae0000">
+  <staging-public-group type="raw" first-id="0x01a80000">
   </staging-public-group>
 
-  <staging-public-group type="bool" first-id="0x01ad0000">
+  <staging-public-group type="bool" first-id="0x01a70000">
   </staging-public-group>
 
-  <staging-public-group type="fraction" first-id="0x01ac0000">
+  <staging-public-group type="fraction" first-id="0x01a60000">
   </staging-public-group>
 
 </resources>
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index f885e31..32aec1a 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -36,6 +36,7 @@
 
 import android.annotation.NonNull;
 import android.app.Activity;
+import android.app.Instrumentation;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
@@ -47,6 +48,7 @@
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -537,6 +539,28 @@
         });
         waitForAfterDraw();
     }
+
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
+    })
+    public void frameRateReset() throws Throwable {
+        mMovingView.setRequestedFrameRate(120f);
+        waitForFrameRateCategoryToSettle();
+        mActivityRule.runOnUiThread(() -> mMovingView.setVisibility(View.INVISIBLE));
+
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+        for (int i = 0; i < 120; i++) {
+            mActivityRule.runOnUiThread(() -> {
+                mMovingView.getParent().onDescendantInvalidated(mMovingView, mMovingView);
+            });
+            instrumentation.waitForIdleSync();
+        }
+
+        assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f);
+    }
+
     private void runAfterDraw(@NonNull Runnable runnable) {
         Handler handler = new Handler(Looper.getMainLooper());
         mAfterDrawLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 9afc4be..5caf77d 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -28,7 +28,6 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
-import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
@@ -1165,34 +1164,15 @@
         });
         waitForAfterDraw();
 
-        // reset the frame rate category counts
-        for (int i = 0; i < 5; i++) {
-            sInstrumentation.runOnMainSync(() -> {
-                mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
-                mView.invalidate();
-            });
-            sInstrumentation.waitForIdleSync();
-        }
-
         // In transition from frequent update to infrequent update
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
             mView.invalidate();
-            runAfterDraw(() -> {
-                assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
-                        mViewRootImpl.getLastPreferredFrameRateCategory());
-            });
-        });
-        waitForAfterDraw();
-        Thread.sleep(delay);
-        sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
-            mView.invalidate();
-            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+            int expected = toolkitFrameRateDefaultNormalReadOnly()
+                    ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+            runAfterDraw(() -> assertEquals(expected,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
-        waitForAfterDraw();
 
         // Infrequent update
         Thread.sleep(delay);
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index 6402206..baab3b2 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -168,6 +168,20 @@
         assertThat(end).isEqualTo("END");
     }
 
+    @Test
+    public void parceling_corruptParcel() {
+        PowerStats stats = new PowerStats(mDescriptor);
+        Parcel parcel = Parcel.obtain();
+        stats.writeToParcel(parcel);
+
+        Parcel newParcel = marshallAndUnmarshall(parcel);
+        newParcel.writeInt(-42);        // Negative section length
+        newParcel.setDataPosition(0);
+
+        PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry);
+        assertThat(newStats).isNull();
+    }
+
     private static Parcel marshallAndUnmarshall(Parcel parcel) {
         byte[] bytes = parcel.marshall();
         parcel.recycle();
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index e1bf40c..6110133 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -105,18 +105,21 @@
     }
 
     @Test
-    fun onDragUpdate_stayOnSameSide() {
+    fun drag_stayOnSameSide() {
         runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
             controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+            controller.onDragEnd()
         }
         waitForAnimateIn()
         assertThat(dropTargetView).isNull()
         assertThat(testListener.locationChanges).isEmpty()
+        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
     }
 
     @Test
-    fun onDragUpdate_toLeft() {
+    fun drag_toLeft() {
+        // Drag to left, but don't finish
         runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
             controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
@@ -132,10 +135,16 @@
             .isEqualTo(expectedDropTargetBounds.height())
 
         assertThat(testListener.locationChanges).containsExactly(BubbleBarLocation.LEFT)
+        assertThat(testListener.locationReleases).isEmpty()
+
+        // Finish the drag
+        runOnMainSync { controller.onDragEnd() }
+        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.LEFT)
     }
 
     @Test
-    fun onDragUpdate_toLeftAndBackToRight() {
+    fun drag_toLeftAndBackToRight() {
+        // Drag to left
         runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
             controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
@@ -143,6 +152,7 @@
         waitForAnimateIn()
         assertThat(dropTargetView).isNotNull()
 
+        // Drag to right
         runOnMainSync { controller.onDragUpdate(pointOnRight.x, pointOnRight.y) }
         // We have to wait for existing drop target to animate out and new to animate in
         waitForAnimateOut()
@@ -158,10 +168,15 @@
 
         assertThat(testListener.locationChanges)
             .containsExactly(BubbleBarLocation.LEFT, BubbleBarLocation.RIGHT)
+        assertThat(testListener.locationReleases).isEmpty()
+
+        // Release the view
+        runOnMainSync { controller.onDragEnd() }
+        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
     }
 
     @Test
-    fun onDragUpdate_toLeftInExclusionRect() {
+    fun drag_toLeftInExclusionRect() {
         runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
             // Exclusion rect is around the bottom center area of the screen
@@ -170,6 +185,10 @@
         waitForAnimateIn()
         assertThat(dropTargetView).isNull()
         assertThat(testListener.locationChanges).isEmpty()
+        assertThat(testListener.locationReleases).isEmpty()
+
+        runOnMainSync { controller.onDragEnd() }
+        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
     }
 
     @Test
@@ -256,8 +275,13 @@
 
     internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
         val locationChanges = mutableListOf<BubbleBarLocation>()
+        val locationReleases = mutableListOf<BubbleBarLocation>()
         override fun onChange(location: BubbleBarLocation) {
             locationChanges.add(location)
         }
+
+        override fun onRelease(location: BubbleBarLocation) {
+            locationReleases.add(location)
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 0297901..f9a1d94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -82,8 +82,8 @@
 
     private BubbleViewProvider mBubble;
     private BubblePositioner mPositioner;
-    private boolean mOnLeft;
-
+    private boolean mBadgeOnLeft;
+    private boolean mDotOnLeft;
     private DotRenderer mDotRenderer;
     private DotRenderer.DrawParams mDrawParams;
     private int mDotColor;
@@ -153,7 +153,8 @@
 
     public void hideDotAndBadge(boolean onLeft) {
         addDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
-        mOnLeft = onLeft;
+        mBadgeOnLeft = onLeft;
+        mDotOnLeft = onLeft;
         hideBadge();
     }
 
@@ -185,7 +186,7 @@
 
         mDrawParams.dotColor = mDotColor;
         mDrawParams.iconBounds = mTempBounds;
-        mDrawParams.leftAlign = mOnLeft;
+        mDrawParams.leftAlign = mDotOnLeft;
         mDrawParams.scale = mDotScale;
 
         mDotRenderer.draw(canvas, mDrawParams);
@@ -255,7 +256,7 @@
      * Whether decorations (badges or dots) are on the left.
      */
     boolean getDotOnLeft() {
-        return mOnLeft;
+        return mDotOnLeft;
     }
 
     /**
@@ -263,7 +264,7 @@
      */
     float[] getDotCenter() {
         float[] dotPosition;
-        if (mOnLeft) {
+        if (mDotOnLeft) {
             dotPosition = mDotRenderer.getLeftDotPosition();
         } else {
             dotPosition = mDotRenderer.getRightDotPosition();
@@ -291,22 +292,23 @@
         if (onLeft != getDotOnLeft()) {
             if (shouldDrawDot()) {
                 animateDotScale(0f /* showDot */, () -> {
-                    mOnLeft = onLeft;
+                    mDotOnLeft = onLeft;
                     invalidate();
                     animateDotScale(1.0f, null /* after */);
                 });
             } else {
-                mOnLeft = onLeft;
+                mDotOnLeft = onLeft;
             }
         }
+        mBadgeOnLeft = onLeft;
         // TODO animate badge
         showBadge();
-
     }
 
     /** Sets the position of the dot and badge. */
     void setDotBadgeOnLeft(boolean onLeft) {
-        mOnLeft = onLeft;
+        mBadgeOnLeft = onLeft;
+        mDotOnLeft = onLeft;
         invalidate();
         showBadge();
     }
@@ -361,7 +363,7 @@
         }
 
         int translationX;
-        if (mOnLeft) {
+        if (mBadgeOnLeft) {
             translationX = -(mBubble.getBubbleIcon().getWidth() - appBadgeBitmap.getWidth());
         } else {
             translationX = 0;
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 d295877..edd5935 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
@@ -474,11 +474,16 @@
 
         mDisplayController.addDisplayChangingController(
                 (displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> {
-                    // This is triggered right before the rotation is applied
-                    if (fromRotation != toRotation) {
+                    Rect newScreenBounds = new Rect();
+                    if (newDisplayAreaInfo != null) {
+                        newScreenBounds =
+                                newDisplayAreaInfo.configuration.windowConfiguration.getBounds();
+                    }
+                    // This is triggered right before the rotation or new screen size is applied
+                    if (fromRotation != toRotation || !newScreenBounds.equals(mScreenBounds)) {
                         if (mStackView != null) {
                             // Layout listener set on stackView will update the positioner
-                            // once the rotation is applied
+                            // once the rotation or screen change is applied
                             mStackView.onOrientationChanged();
                         }
                     }
@@ -725,6 +730,17 @@
         }
     }
 
+    /**
+     * Animate bubble bar to the given location. The location change is transient. It does not
+     * update the state of the bubble bar.
+     * To update bubble bar pinned location, use {@link #setBubbleBarLocation(BubbleBarLocation)}.
+     */
+    public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+        if (canShowAsBubbleBar()) {
+            mBubbleStateListener.animateBubbleBarLocation(bubbleBarLocation);
+        }
+    }
+
     /** Whether this userId belongs to the current user. */
     private boolean isCurrentProfile(int userId) {
         return userId == UserHandle.USER_ALL
@@ -2250,15 +2266,19 @@
         private final SingleInstanceRemoteListener<BubbleController, IBubblesListener> mListener;
         private final Bubbles.BubbleStateListener mBubbleListener =
                 new Bubbles.BubbleStateListener() {
+                    @Override
+                    public void onBubbleStateChange(BubbleBarUpdate update) {
+                        Bundle b = new Bundle();
+                        b.setClassLoader(BubbleBarUpdate.class.getClassLoader());
+                        b.putParcelable(BubbleBarUpdate.BUNDLE_KEY, update);
+                        mListener.call(l -> l.onBubbleStateChange(b));
+                    }
 
-            @Override
-            public void onBubbleStateChange(BubbleBarUpdate update) {
-                Bundle b = new Bundle();
-                b.setClassLoader(BubbleBarUpdate.class.getClassLoader());
-                b.putParcelable(BubbleBarUpdate.BUNDLE_KEY, update);
-                mListener.call(l -> l.onBubbleStateChange(b));
-            }
-        };
+                    @Override
+                    public void animateBubbleBarLocation(BubbleBarLocation location) {
+                        mListener.call(l -> l.animateBubbleBarLocation(location));
+                    }
+                };
 
         IBubblesImpl(BubbleController controller) {
             mController = controller;
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 127a49f..322088b 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
@@ -37,6 +37,7 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 
@@ -304,6 +305,12 @@
          * Called when the bubbles state changes.
          */
         void onBubbleStateChange(BubbleBarUpdate update);
+
+        /**
+         * Called when bubble bar should temporarily be animated to a new location.
+         * Does not result in a state change.
+         */
+        void animateBubbleBarLocation(BubbleBarLocation location);
     }
 
     /** Listener to find out about stack expansion / collapse events. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index e48f8d5..14d29cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -15,8 +15,9 @@
  */
 
 package com.android.wm.shell.bubbles;
-import android.os.Bundle;
 
+import android.os.Bundle;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 /**
  * Listener interface that Launcher attaches to SystemUI to get bubbles callbacks.
  */
@@ -26,4 +27,10 @@
      * Called when the bubbles state changes.
      */
     void onBubbleStateChange(in Bundle update);
+
+    /**
+     * Called when bubble bar should temporarily be animated to a new location.
+     * Does not result in a state change.
+     */
+    void animateBubbleBarLocation(in BubbleBarLocation location);
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index fe9c4d4..a51ac63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -135,9 +135,9 @@
 
         private fun finishDrag() {
             if (!isStuckToDismiss) {
-                animationHelper.animateToRestPosition()
                 pinController.onDragEnd()
                 dragListener.onReleased(inDismiss = false)
+                animationHelper.animateToRestPosition()
                 dismissView.hide()
             }
             isMoving = false
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 62cc4da..a351cef 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
@@ -33,6 +33,8 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.BubbleData;
@@ -42,6 +44,8 @@
 import com.android.wm.shell.bubbles.DeviceConfig;
 import com.android.wm.shell.bubbles.DismissViewUtils;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
+import com.android.wm.shell.common.bubbles.BaseBubblePinController;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 import com.android.wm.shell.common.bubbles.DismissView;
 
 import kotlin.Unit;
@@ -115,7 +119,18 @@
 
         mBubbleExpandedViewPinController = new BubbleExpandedViewPinController(
                 context, this, mPositioner);
-        mBubbleExpandedViewPinController.setListener(mBubbleController::setBubbleBarLocation);
+        mBubbleExpandedViewPinController.setListener(
+                new BaseBubblePinController.LocationChangeListener() {
+                    @Override
+                    public void onChange(@NonNull BubbleBarLocation bubbleBarLocation) {
+                        mBubbleController.animateBubbleBarLocation(bubbleBarLocation);
+                    }
+
+                    @Override
+                    public void onRelease(@NonNull BubbleBarLocation location) {
+                        mBubbleController.setBubbleBarLocation(location);
+                    }
+                });
 
         setOnClickListener(view -> hideMenuOrCollapse());
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
index a008045..e514f9d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
@@ -82,6 +82,7 @@
     fun onDragEnd() {
         getDropTargetView()?.let { view -> view.animateOut { removeDropTargetView(view) } }
         dismissZone = null
+        listener?.onRelease(if (onLeft) LEFT else RIGHT)
     }
 
     /**
@@ -170,14 +171,22 @@
     /** Receive updates on location changes */
     interface LocationChangeListener {
         /**
-         * Bubble bar [BubbleBarLocation] has changed as a result of dragging
+         * Bubble bar has been dragged to a new [BubbleBarLocation]. And the drag is still in
+         * progress.
          *
          * Triggered when drag gesture passes the middle of the screen and before touch up. Can be
          * triggered multiple times per gesture.
          *
          * @param location new location as a result of the ongoing drag operation
          */
-        fun onChange(location: BubbleBarLocation)
+        fun onChange(location: BubbleBarLocation) {}
+
+        /**
+         * Bubble bar has been released in the [BubbleBarLocation].
+         *
+         * @param location final location of the bubble bar once drag is released
+         */
+        fun onRelease(location: BubbleBarLocation)
     }
 
     companion object {
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 0a9e5d0..08b7c01 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
@@ -158,6 +158,10 @@
             com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
         )
 
+    /** Task id of the task currently being dragged from fullscreen/split. */
+    val draggingTaskId
+        get() = dragToDesktopTransitionHandler.draggingTaskId
+
     private var recentsAnimationRunning = false
     private lateinit var splitScreenController: SplitScreenController
 
@@ -406,6 +410,7 @@
     fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
         val wct = WindowContainerTransaction()
         wct.setBounds(taskInfo.token, Rect())
+        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED)
         shellTaskOrganizer.applyTransaction(wct)
     }
 
@@ -447,7 +452,9 @@
         )
         val wct = WindowContainerTransaction()
         wct.setBounds(task.token, Rect())
-        addMoveToSplitChanges(wct, task)
+        // Rather than set windowing mode to multi-window at task level, set it to
+        // undefined and inherit from split stage.
+        wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
         } else {
@@ -458,10 +465,12 @@
     private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
         if (splitScreenController.isTaskInSplitScreen(taskInfo.taskId)) {
             splitScreenController.prepareExitSplitScreen(
-                    wct,
-                    splitScreenController.getStageOfTask(taskInfo.taskId),
-                    EXIT_REASON_DESKTOP_MODE
+                wct,
+                splitScreenController.getStageOfTask(taskInfo.taskId),
+                EXIT_REASON_DESKTOP_MODE
             )
+            splitScreenController.transitionHandler
+                ?.onSplitToDesktop()
         }
     }
 
@@ -1044,9 +1053,11 @@
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
     ) {
-        // Explicitly setting multi-window at task level interferes with animations.
-        // Let task inherit windowing mode once transition is complete instead.
-        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED)
+        // This windowing mode is to get the transition animation started; once we complete
+        // split select, we will change windowing mode to undefined and inherit from split stage.
+        // Going to undefined here causes task to flicker to the top left.
+        // Cancelling the split select flow will revert it to fullscreen.
+        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
         // The task's density may have been overridden in freeform; revert it here as we don't
         // want it overridden in multi-window.
         wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
@@ -1237,7 +1248,7 @@
                 finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
             }
             DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
-                    DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+            DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
                 cancelDragToDesktop(taskInfo)
             }
             DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index e341f2d..e5e435d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -6,6 +6,7 @@
 import android.animation.ValueAnimator
 import android.app.ActivityOptions
 import android.app.ActivityOptions.SourceInfo
+import android.app.ActivityTaskManager.INVALID_TASK_ID
 import android.app.PendingIntent
 import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
 import android.app.PendingIntent.FLAG_MUTABLE
@@ -26,6 +27,9 @@
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.splitscreen.SplitScreenController
@@ -68,7 +72,7 @@
             .addCategory(Intent.CATEGORY_HOME)
 
     private var dragToDesktopStateListener: DragToDesktopStateListener? = null
-    private var splitScreenController: SplitScreenController? = null
+    private lateinit var splitScreenController: SplitScreenController
     private var transitionState: TransitionState? = null
     private lateinit var onTaskResizeAnimationListener: OnTaskResizeAnimationListener
 
@@ -76,6 +80,9 @@
     val inProgress: Boolean
         get() = transitionState != null
 
+    /** The task id of the task currently being dragged from fullscreen/split. */
+    val draggingTaskId: Int
+        get() = transitionState?.draggedTaskId ?: INVALID_TASK_ID
     /** Sets a listener to receive callback about events during the transition animation. */
     fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
         dragToDesktopStateListener = listener
@@ -130,10 +137,14 @@
                 .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
 
         transitionState = if (isSplitTask(taskId)) {
+            val otherTask = getOtherSplitTask(taskId) ?: throw IllegalStateException(
+                "Expected split task to have a counterpart."
+            )
             TransitionState.FromSplit(
                     draggedTaskId = taskId,
                     dragAnimator = dragToDesktopAnimator,
-                    startTransitionToken = startTransitionToken
+                    startTransitionToken = startTransitionToken,
+                    otherSplitTask = otherTask
             )
         } else {
             TransitionState.FromFullscreen(
@@ -347,6 +358,12 @@
                 ?: error("Start transition expected to be waiting for merge but wasn't")
         if (isEndTransition) {
             info.changes.withIndex().forEach { (i, change) ->
+                // If we're exiting split, hide the remaining split task.
+                if (state is TransitionState.FromSplit &&
+                    change.taskInfo?.taskId == state.otherSplitTask) {
+                    t.hide(change.leash)
+                    startTransactionFinishT.hide(change.leash)
+                }
                 if (change.mode == TRANSIT_CLOSE) {
                     t.hide(change.leash)
                     startTransactionFinishT.hide(change.leash)
@@ -392,7 +409,6 @@
             onTaskResizeAnimationListener.onAnimationStart(state.draggedTaskId, t,
                 unscaledStartBounds)
             finishCallback.onTransitionFinished(null /* wct */)
-
             val tx: SurfaceControl.Transaction = transactionSupplier.get()
             ValueAnimator.ofObject(rectEvaluator, unscaledStartBounds, endBounds)
                     .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
@@ -549,7 +565,18 @@
     }
 
     private fun isSplitTask(taskId: Int): Boolean {
-        return splitScreenController?.isTaskInSplitScreen(taskId) ?: false
+        return splitScreenController.isTaskInSplitScreen(taskId)
+    }
+
+    private fun getOtherSplitTask(taskId: Int): Int? {
+        val splitPos = splitScreenController.getSplitPosition(taskId)
+        if (splitPos == SPLIT_POSITION_UNDEFINED) return null
+        val otherTaskPos = if (splitPos == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+            SPLIT_POSITION_TOP_OR_LEFT
+        } else {
+            SPLIT_POSITION_BOTTOM_OR_RIGHT
+        }
+        return splitScreenController.getTaskInfo(otherTaskPos)?.taskId
     }
 
     private fun requireTransitionState(): TransitionState {
@@ -598,6 +625,7 @@
                 override var cancelled: Boolean = false,
                 override var startAborted: Boolean = false,
                 var splitRootChange: Change? = null,
+                var otherSplitTask: Int
         ) : TransitionState()
     }
 
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 4c68106..2a50b19 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
@@ -3351,6 +3351,11 @@
                 true /* reparentLeafTaskIfRelaunch */);
     }
 
+    /** Call this when the animation from split screen to desktop is started. */
+    public void onSplitToDesktop() {
+        setSplitsVisible(false);
+    }
+
     /** Call this when the recents animation finishes by doing pair-to-pair switch. */
     public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
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 922c55f..01175f5 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -34,6 +35,7 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.compatui.AppCompatUtils.isSingleTopActivityTranslucent;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -272,10 +274,9 @@
         mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() {
             @Override
             public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
-                if (visible) {
+                if (visible && stage != STAGE_TYPE_UNDEFINED) {
                     DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
-                    if (decor != null && DesktopModeStatus.isEnabled()
-                            && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                    if (decor != null && DesktopModeStatus.isEnabled()) {
                         mDesktopTasksController.moveToSplit(decor.mTaskInfo);
                     }
                 }
@@ -915,6 +916,11 @@
 
     @Nullable
     private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
+        // If we are mid-transition, dragged task's decor is always relevant.
+        final int draggedTaskId = mDesktopTasksController.getDraggingTaskId();
+        if (draggedTaskId != INVALID_TASK_ID) {
+            return mWindowDecorByTaskId.get(draggedTaskId);
+        }
         final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
         if (focusedDecor == null) {
             return null;
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 4981029..5f84862 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -80,10 +80,13 @@
 }
 
 flag {
-    name: "subscriptions_listener_thread"
+    name: "subscriptions_changed_listener_thread"
     namespace: "location"
-    description: "Flag for running onSubscriptionsChangeListener on FgThread"
+    description: "Flag for running onSubscriptionsChangedListener on FgThread"
     bug: "332451908"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
 
 flag {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1515811..adbfc72 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1745,4 +1745,7 @@
     <string name="feminine">Feminine</string>
     <!-- List entry in developer settings to set the grammatical gender to Masculine [CHAR LIMIT=30]-->
     <string name="masculine">Masculine</string>
+
+    <!-- The name of the screen for seeing and installing system updates. [CHAR LIMIT=40]-->
+    <string name="system_update_settings_list_item_title">System Updates</string>
 </resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
index 64dcf6e..395354e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -34,6 +34,7 @@
         "compatibility-device-util-axt",
         "platform-test-annotations",
         "truth",
+        "uiautomator-helpers",
     ],
     srcs: [
         "src/**/*.java",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 0ab99fa..66943d4 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -30,6 +30,8 @@
 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
 import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Instrumentation;
 import android.app.KeyguardManager;
@@ -43,6 +45,8 @@
 import android.hardware.display.DisplayManager;
 import android.media.AudioManager;
 import android.os.PowerManager;
+import android.os.RemoteException;
+import android.platform.uiautomator_helpers.WaitUtils;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
@@ -51,6 +55,8 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.UiDevice;
 
 import com.android.compatibility.common.util.TestUtils;
 import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
@@ -60,7 +66,6 @@
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -76,11 +81,13 @@
     private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
     private static final int TIMEOUT_UI_CHANGE_S = 5;
     private static final int NO_GLOBAL_ACTION = -1;
-    private static final Intent INTENT_OPEN_MENU = new Intent(INTENT_TOGGLE_MENU)
-            .setPackage(PACKAGE_NAME);
+    private static final Intent INTENT_OPEN_MENU =
+            new Intent(INTENT_TOGGLE_MENU).setPackage(PACKAGE_NAME);
+    private static final String SERVICE_NAME = PACKAGE_NAME + "/.AccessibilityMenuService";
 
     private static Instrumentation sInstrumentation;
     private static UiAutomation sUiAutomation;
+    private static UiDevice sUiDevice;
     private static final AtomicInteger sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION);
     private static final AtomicBoolean sOpenBlocked = new AtomicBoolean(false);
 
@@ -91,12 +98,14 @@
 
     @BeforeClass
     public static void classSetup() throws Throwable {
-        final String serviceName = PACKAGE_NAME + "/.AccessibilityMenuService";
+        Configurator.getInstance()
+                .setUiAutomationFlags(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
         sUiAutomation = sInstrumentation.getUiAutomation(
                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
         sUiAutomation.adoptShellPermissionIdentity(
                 UiAutomation.ALL_PERMISSIONS.toArray(new String[0]));
+        sUiDevice = UiDevice.getInstance(sInstrumentation);
 
         final Context context = sInstrumentation.getTargetContext();
         sAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -117,13 +126,13 @@
 
         // Enable a11yMenu service.
         Settings.Secure.putString(context.getContentResolver(),
-                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, serviceName);
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, SERVICE_NAME);
 
         TestUtils.waitUntil("Failed to enable service",
                 TIMEOUT_SERVICE_STATUS_CHANGE_S,
                 () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
-                                info -> info.getId().contains(serviceName)).count() == 1);
+                                info -> info.getId().contains(SERVICE_NAME)).count() == 1);
         context.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -159,8 +168,11 @@
     public void tearDown() throws Throwable {
         closeMenu();
         sLastGlobalAction.set(NO_GLOBAL_ACTION);
+        // Leave the device in clean state when the test finished
+        unlockSignal();
         // dismisses screenshot popup if present.
-        sUiAutomation.executeShellCommand("input keyevent KEYCODE_BACK");
+        sUiDevice.pressBack();
+        sUiDevice.pressHome();
     }
 
     private static boolean isMenuVisible() {
@@ -168,38 +180,25 @@
         return root != null && root.getPackageName().toString().equals(PACKAGE_NAME);
     }
 
-    private static void wakeUpScreen() throws Throwable {
-        sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP");
-        TestUtils.waitUntil("Screen did not wake up.",
-                TIMEOUT_UI_CHANGE_S,
-                () -> sPowerManager.isInteractive());
+    private static void wakeUpScreen() throws RemoteException {
+        sUiDevice.wakeUp();
+        WaitUtils.waitForValueToSettle("Screen On", AccessibilityMenuServiceTest::isScreenOn);
+        assertWithMessage("Screen is on").that(isScreenOn()).isTrue();
     }
 
     private static void closeScreen() throws Throwable {
-        Display display = sDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
         sUiAutomation.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN);
-        TestUtils.waitUntil("Screen did not close.",
-                TIMEOUT_UI_CHANGE_S,
-                () -> !sPowerManager.isInteractive()
-                        && display.getState() == Display.STATE_OFF
-        );
+        WaitUtils.waitForValueToSettle("Screen Off", AccessibilityMenuServiceTest::isScreenOff);
+        assertWithMessage("Screen is off").that(isScreenOff()).isTrue();
     }
 
     private static void openMenu() throws Throwable {
         unlockSignal();
-        sInstrumentation.getContext().sendBroadcast(INTENT_OPEN_MENU);
-
-        TestUtils.waitUntil("Timed out before menu could appear.",
-                TIMEOUT_UI_CHANGE_S,
-                () -> {
-                    if (isMenuVisible()) {
-                        return true;
-                    } else {
-                        unlockSignal();
-                        sInstrumentation.getContext().sendBroadcast(INTENT_OPEN_MENU);
-                        return false;
-                    }
-                });
+        if (!isMenuVisible()) {
+            sInstrumentation.getTargetContext().sendBroadcast(INTENT_OPEN_MENU);
+            sUiDevice.waitForIdle();
+            WaitUtils.ensureThat("Accessibility Menu is visible", () -> isMenuVisible());
+        }
     }
 
     private static void closeMenu() throws Throwable {
@@ -342,7 +341,9 @@
 
         sUiAutomation.executeAndWaitForEvent(
                 () -> assistantButton.performAction(CLICK_ID),
-                (event) -> expectedPackage.contains(event.getPackageName()),
+                (event) ->
+                        event.getPackageName() != null
+                                && expectedPackage.contains(event.getPackageName()),
                 TIMEOUT_UI_CHANGE_S * 1000
         );
     }
@@ -358,7 +359,9 @@
 
         sUiAutomation.executeAndWaitForEvent(
                 () -> settingsButton.performAction(CLICK_ID),
-                (event) -> expectedPackage.contains(event.getPackageName()),
+                (event) ->
+                        event.getPackageName() != null
+                                && expectedPackage.contains(event.getPackageName()),
                 TIMEOUT_UI_CHANGE_S * 1000
         );
     }
@@ -454,24 +457,40 @@
     }
 
     @Test
-    @Ignore("Test failure in pre/postsubmit cannot be replicated on local devices. "
-            + "Coverage is low-impact.")
     public void testOnScreenLock_cannotOpenMenu() throws Throwable {
         closeScreen();
         wakeUpScreen();
+        sInstrumentation.getContext().sendBroadcast(INTENT_OPEN_MENU);
+        sUiDevice.waitForIdle();
 
         TestUtils.waitUntil("Did not receive signal that menu cannot open",
                 TIMEOUT_UI_CHANGE_S,
-                () -> {
-                    sInstrumentation.getContext().sendBroadcast(INTENT_OPEN_MENU);
-                    return sOpenBlocked.get();
-                });
+                sOpenBlocked::get);
     }
 
-    private static void unlockSignal() {
-        // MENU unlocks screen,
-        // BACK closes any menu that may appear if the screen wasn't locked.
-        sUiAutomation.executeShellCommand("input keyevent KEYCODE_MENU");
-        sUiAutomation.executeShellCommand("input keyevent KEYCODE_BACK");
+    private static void unlockSignal() throws RemoteException {
+        if (!sKeyguardManager.isKeyguardLocked()) {
+            return;
+        }
+        // go/adb-cheats#unlock-screen
+        wakeUpScreen();
+        if (sKeyguardManager.isKeyguardLocked()) {
+            sUiDevice.pressMenu();
+        }
+        WaitUtils.ensureThat(
+                "Device unlocked & isInteractive",
+                () -> isScreenOn() && !sKeyguardManager.isKeyguardLocked());
+    }
+
+    private static boolean isScreenOn() {
+        int display = Display.DEFAULT_DISPLAY;
+        return sPowerManager.isInteractive(display)
+                && sDisplayManager.getDisplay(display).getState() == Display.STATE_ON;
+    }
+
+    private static boolean isScreenOff() {
+        int display = Display.DEFAULT_DISPLAY;
+        return !sPowerManager.isInteractive(display)
+                && sDisplayManager.getDisplay(display).getState() == Display.STATE_OFF;
     }
 }
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 338987a..7d56a67 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
@@ -20,6 +20,8 @@
 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 androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.core.animateFloatAsState
@@ -115,8 +117,6 @@
 import androidx.compose.ui.window.Popup
 import androidx.core.view.setPadding
 import androidx.window.layout.WindowMetricsCalculator
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
@@ -300,7 +300,7 @@
                             viewModel.onHidePopup()
                             viewModel.onOpenWidgetEditor(selectedKey.value)
                         },
-                        onHide = { viewModel.onHidePopup()}
+                        onHide = { viewModel.onHidePopup() }
                     )
                 }
                 null -> {}
@@ -374,7 +374,7 @@
             liveContentKeys.indexOfFirst { !prevLiveContentKeys.contains(it) }
 
         // Scroll if current position is behind the first updated content
-        if (indexOfFirstUpdatedContent in 0..<gridState.firstVisibleItemIndex) {
+        if (indexOfFirstUpdatedContent in 0 until gridState.firstVisibleItemIndex) {
             // Launching with a scope to prevent the job from being canceled in the case of a
             // recomposition during scrolling
             coroutineScope.launch { gridState.animateScrollToItem(indexOfFirstUpdatedContent) }
@@ -841,6 +841,8 @@
     widgetConfigurator: WidgetConfigurator?,
     modifier: Modifier = Modifier,
 ) {
+    val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+
     Box(
         modifier =
             modifier.thenIf(!viewModel.isEditMode && model.inQuietMode) {
@@ -865,6 +867,16 @@
                         setPadding(0)
                     }
             },
+            update = {
+                it.apply {
+                    importantForAccessibility =
+                        if (isFocusable) {
+                            IMPORTANT_FOR_ACCESSIBILITY_AUTO
+                        } else {
+                            IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                        }
+                }
+            },
             // For reusing composition in lazy lists.
             onReset = {},
         )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
index f354b80..74af3ca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -98,10 +98,10 @@
     }
 
     override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
-        return onClick != null || super.onInterceptTouchEvent(ev)
+        return (isSliceViewClickable && onClick != null) || super.onInterceptTouchEvent(ev)
     }
 
     override fun onClick(v: View?) {
-        onClick?.let { it() } ?: super.onClick(v)
+        onClick?.takeIf { isSliceViewClickable }?.let { it() } ?: super.onClick(v)
     }
 }
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 4f3a6c8..874c0a2 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
@@ -38,6 +38,8 @@
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.toggleableState
+import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.unit.dp
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
@@ -79,6 +81,12 @@
                     modifier =
                         Modifier.fillMaxSize().padding(8.dp).semantics {
                             role = Role.Switch
+                            toggleableState =
+                                if (viewModel.isActive) {
+                                    ToggleableState.On
+                                } else {
+                                    ToggleableState.Off
+                                }
                             contentDescription = label
                         },
                     onClick = { onCheckedChange(!viewModel.isActive) },
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 4273b4f..ca64323 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
@@ -217,18 +217,18 @@
         maybePruneMaps(layoutImpl, prevElement, prevSceneState)
     }
 
-    override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
+    override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
         // TODO(b/324191441): Investigate whether making this check more complex (checking if this
         // element is shared or transformed) would lead to better performance.
-        return layoutImpl.state.currentTransitions.isEmpty()
+        return layoutImpl.state.isTransitioning()
     }
 
-    override fun Placeable.PlacementScope.isPlacementApproachComplete(
+    override fun Placeable.PlacementScope.isPlacementApproachInProgress(
         lookaheadCoordinates: LayoutCoordinates
     ): Boolean {
         // TODO(b/324191441): Investigate whether making this check more complex (checking if this
         // element is shared or transformed) would lead to better performance.
-        return layoutImpl.state.currentTransitions.isEmpty()
+        return layoutImpl.state.isTransitioning()
     }
 
     @ExperimentalComposeUiApi
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 7fb5a4d..339868c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -26,7 +26,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.zIndex
@@ -74,7 +74,9 @@
         Box(
             modifier
                 .zIndex(zIndex)
-                .intermediateLayout { measurable, constraints ->
+                .approachLayout(
+                    isMeasurementApproachInProgress = { scope.layoutState.isTransitioning() }
+                ) { measurable, constraints ->
                     targetSize = lookaheadSize
                     val placeable = measurable.measure(constraints)
                     layout(placeable.width, placeable.height) { placeable.place(0, 0) }
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 ad691ba..d383cec 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
@@ -251,8 +251,8 @@
 
 private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
     Modifier.Node(), ApproachLayoutModifierNode {
-    override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
-        return layoutImpl.state.currentTransition == null
+    override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
+        return layoutImpl.state.isTransitioning()
     }
 
     @ExperimentalComposeUiApi
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
index bd36cb8..b392c67 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
@@ -18,15 +18,17 @@
 
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.unit.Constraints
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 
 @OptIn(ExperimentalComposeUiApi::class)
 internal fun Modifier.noResizeDuringTransitions(layoutState: SceneTransitionLayoutState): Modifier {
-    return intermediateLayout { measurable, constraints ->
+    return approachLayout(isMeasurementApproachInProgress = { layoutState.isTransitioning() }) {
+        measurable,
+        constraints ->
         if (layoutState.currentTransition == null) {
-            return@intermediateLayout measurable.measure(constraints).run {
+            return@approachLayout measurable.measure(constraints).run {
                 layout(width, height) { place(0, 0) }
             }
         }
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 b1d7055..92e1b2c 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
@@ -46,7 +46,7 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
@@ -91,7 +91,9 @@
             modifier
                 .offset(offset)
                 .element(key)
-                .intermediateLayout { measurable, constraints ->
+                .approachLayout(
+                    isMeasurementApproachInProgress = { layoutState.isTransitioning() }
+                ) { measurable, constraints ->
                     onLayout()
                     val placement = measurable.measure(constraints)
                     layout(placement.width, placement.height) {
@@ -525,7 +527,7 @@
                     // page should be composed.
                     HorizontalPager(
                         pagerState,
-                        outOfBoundsPageCount = 0,
+                        beyondViewportPageCount = 0,
                     ) { page ->
                         when (page) {
                             0 -> Box(Modifier.element(TestElements.Foo).fillMaxSize())
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index ac7717b..ce4c5275 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -202,11 +202,11 @@
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
-        fun data(): List<TestCase> =
-            listOf(
-                TestCase(NestedScrollSource.Drag),
-                TestCase(NestedScrollSource.Fling),
-                TestCase(NestedScrollSource.Wheel),
+        fun data(): List<TestCase> {
+            return listOf(
+                TestCase(NestedScrollSource.UserInput),
+                TestCase(NestedScrollSource.SideEffect),
             )
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index c96a8ce..9e9a002 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -24,17 +24,21 @@
 import android.provider.Settings
 import android.widget.RemoteViews
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
@@ -45,8 +49,14 @@
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
@@ -63,6 +73,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -94,6 +105,8 @@
     private lateinit var mediaRepository: FakeCommunalMediaRepository
     private lateinit var userRepository: FakeUserRepository
     private lateinit var shadeTestUtil: ShadeTestUtil
+    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+    private lateinit var communalRepository: FakeCommunalRepository
 
     private lateinit var underTest: CommunalViewModel
 
@@ -106,12 +119,14 @@
         MockitoAnnotations.initMocks(this)
 
         keyguardRepository = kosmos.fakeKeyguardRepository
+        keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
         tutorialRepository = kosmos.fakeCommunalTutorialRepository
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         smartspaceRepository = kosmos.fakeSmartspaceRepository
         mediaRepository = kosmos.fakeCommunalMediaRepository
         userRepository = kosmos.fakeUserRepository
         shadeTestUtil = kosmos.shadeTestUtil
+        communalRepository = kosmos.fakeCommunalRepository
 
         kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
@@ -125,6 +140,7 @@
         underTest =
             CommunalViewModel(
                 testScope,
+                kosmos.keyguardTransitionInteractor,
                 kosmos.communalInteractor,
                 kosmos.communalTutorialInteractor,
                 kosmos.shadeInteractor,
@@ -326,6 +342,105 @@
             assertThat(underTest.canChangeScene()).isFalse()
         }
 
+    @Test
+    fun isFocusable_isFalse_whenTransitioningAwayFromGlanceableHub() =
+        testScope.runTest {
+            val isFocusable by collectLastValue(underTest.isFocusable)
+
+            // Shade not expanded.
+            shadeTestUtil.setLockscreenShadeExpansion(0f)
+            // On communal scene.
+            communalRepository.setTransitionState(
+                flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+            )
+            // Open bouncer.
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+
+            keyguardTransitionRepository.sendTransitionStep(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.PRIMARY_BOUNCER,
+                transitionState = TransitionState.RUNNING,
+                value = 0.5f,
+            )
+            assertThat(isFocusable).isEqualTo(false)
+
+            // Transitioned to bouncer.
+            keyguardTransitionRepository.sendTransitionStep(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.PRIMARY_BOUNCER,
+                transitionState = TransitionState.FINISHED,
+                value = 1f,
+            )
+            assertThat(isFocusable).isEqualTo(false)
+        }
+
+    @Test
+    fun isFocusable_isFalse_whenNotOnCommunalScene() =
+        testScope.runTest {
+            val isFocusable by collectLastValue(underTest.isFocusable)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            shadeTestUtil.setLockscreenShadeExpansion(0f)
+            // Transitioned away from communal scene.
+            communalRepository.setTransitionState(
+                flowOf(ObservableTransitionState.Idle(CommunalScenes.Blank))
+            )
+
+            assertThat(isFocusable).isEqualTo(false)
+        }
+
+    @Test
+    fun isFocusable_isTrue_whenIdleOnCommunal_andShadeNotExpanded() =
+        testScope.runTest {
+            val isFocusable by collectLastValue(underTest.isFocusable)
+
+            // On communal scene.
+            communalRepository.setTransitionState(
+                flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+            )
+            // Transitioned to Glanceable hub.
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            // Shade not expanded.
+            shadeTestUtil.setLockscreenShadeExpansion(0f)
+
+            assertThat(isFocusable).isEqualTo(true)
+        }
+
+    @Test
+    fun isFocusable_isFalse_whenQsIsExpanded() =
+        testScope.runTest {
+            val isFocusable by collectLastValue(underTest.isFocusable)
+
+            // On communal scene.
+            communalRepository.setTransitionState(
+                flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+            )
+            // Transitioned to Glanceable hub.
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            // Qs is expanded.
+            shadeTestUtil.setQsExpansion(1f)
+
+            assertThat(isFocusable).isEqualTo(false)
+        }
+
     private suspend fun setIsMainUser(isMainUser: Boolean) {
         whenever(user.isMain).thenReturn(isMainUser)
         userRepository.setUserInfos(listOf(user))
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 2fb8212..f58e01f 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
@@ -30,6 +30,7 @@
 import com.android.systemui.scene.data.repository.sceneContainerRepository
 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.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.testKosmos
@@ -39,7 +40,6 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -54,19 +54,46 @@
 
     private lateinit var underTest: SceneInteractor
 
-    @Before
-    fun setUp() {
-        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 previousScene = currentScene
+            assertThat(previousScene).isNotEqualTo(Scenes.Bouncer)
+            underTest.changeScene(Scenes.Bouncer, "reason")
+            assertThat(currentScene).isEqualTo(previousScene)
+        }
+
+    @Test
     fun changeScene() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
+
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -77,6 +104,8 @@
     @Test
     fun changeScene_toGoneWhenUnl_doesNotThrow() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
+
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -91,11 +120,15 @@
 
     @Test(expected = IllegalStateException::class)
     fun changeScene_toGoneWhenStillLocked_throws() =
-        testScope.runTest { underTest.changeScene(Scenes.Gone, "reason") }
+        testScope.runTest {
+            underTest = kosmos.sceneInteractor
+            underTest.changeScene(Scenes.Gone, "reason")
+        }
 
     @Test
     fun sceneChanged_inDataSource() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -107,6 +140,7 @@
     @Test
     fun transitionState() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val underTest = kosmos.sceneContainerRepository
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
@@ -143,6 +177,7 @@
     @Test
     fun transitioningTo() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(underTest.currentScene.value)
@@ -179,6 +214,7 @@
     @Test
     fun isTransitionUserInputOngoing_idle_false() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(Scenes.Shade)
@@ -193,6 +229,7 @@
     @Test
     fun isTransitionUserInputOngoing_transition_true() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Transition(
@@ -213,6 +250,7 @@
     @Test
     fun isTransitionUserInputOngoing_updateMidTransition_false() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Transition(
@@ -244,6 +282,7 @@
     @Test
     fun isTransitionUserInputOngoing_updateOnIdle_false() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Transition(
@@ -268,6 +307,7 @@
     @Test
     fun isVisible() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isTrue()
 
@@ -281,6 +321,7 @@
     @Test
     fun isVisible_duringRemoteUserInteraction_forcedVisible() =
         testScope.runTest {
+            underTest = kosmos.sceneInteractor
             underTest.setVisible(false, "reason")
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isFalse()
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index d13efd2..f644584f 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -174,7 +174,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="24dp"
-        android:accessibilityLiveRegion="polite"
+        android:accessibilityLiveRegion="assertive"
         android:fadingEdge="horizontal"
         android:gravity="center_horizontal"
         android:scrollHorizontally="true"
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index a6e660f..46b8e46 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -152,7 +152,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="24dp"
-        android:accessibilityLiveRegion="polite"
+        android:accessibilityLiveRegion="assertive"
         android:fadingEdge="horizontal"
         android:gravity="center_horizontal"
         android:scrollHorizontally="true"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index c724d24..d51fe58 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -150,7 +150,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="24dp"
-        android:accessibilityLiveRegion="polite"
+        android:accessibilityLiveRegion="assertive"
         android:fadingEdge="horizontal"
         android:gravity="center_horizontal"
         android:scrollHorizontally="true"
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index 79b82bf..7adfa6c 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -20,133 +20,138 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-    <FrameLayout
-        android:id="@+id/actions_container_background"
-        android:visibility="gone"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:elevation="4dp"
-        android:background="@drawable/shelf_action_chip_container_background"
-        android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
-        android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/guideline"
-        >
-        <HorizontalScrollView
-            android:id="@+id/actions_container"
-            android:layout_width="wrap_content"
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/screenshot_static"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <FrameLayout
+            android:id="@+id/actions_container_background"
+            android:visibility="gone"
             android:layout_height="wrap_content"
-            android:layout_marginVertical="@dimen/overlay_action_container_padding_vertical"
-            android:layout_marginHorizontal="@dimen/overlay_action_chip_margin_start"
-            android:background="@drawable/shelf_action_container_clipping_shape"
-            android:clipToOutline="true"
-            android:scrollbars="none">
-            <LinearLayout
-                android:id="@+id/screenshot_actions"
+            android:layout_width="wrap_content"
+            android:elevation="4dp"
+            android:background="@drawable/shelf_action_chip_container_background"
+            android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
+            android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/guideline"
+            >
+            <HorizontalScrollView
+                android:id="@+id/actions_container"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:showDividers="middle"
-                android:divider="@drawable/shelf_action_chip_divider"
-                android:animateLayoutChanges="true"
-                />
-        </HorizontalScrollView>
-    </FrameLayout>
-    <View
-        android:id="@+id/screenshot_preview_border"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
-        android:layout_marginTop="@dimen/overlay_border_width_neg"
-        android:layout_marginEnd="@dimen/overlay_border_width_neg"
-        android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
-        android:elevation="4dp"
-        android:background="@drawable/overlay_border"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="@id/screenshot_preview"
-        app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
-        app:layout_constraintBottom_toTopOf="@id/actions_container_background"/>
-    <ImageView
-        android:id="@+id/screenshot_preview"
-        android:layout_width="@dimen/overlay_x_scale"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/overlay_border_width"
-        android:layout_marginBottom="@dimen/overlay_border_width"
-        android:layout_gravity="center"
-        android:elevation="4dp"
-        android:contentDescription="@string/screenshot_edit_description"
-        android:scaleType="fitEnd"
-        android:background="@drawable/overlay_preview_background"
-        android:adjustViewBounds="true"
-        android:clickable="true"
-        app:layout_constraintStart_toStartOf="@id/screenshot_preview_border"
-        app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"/>
-    <ImageView
-        android:id="@+id/screenshot_badge"
-        android:layout_width="56dp"
-        android:layout_height="56dp"
-        android:visibility="gone"
-        android:elevation="5dp"
-        app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
-        app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"/>
-    <FrameLayout
-        android:id="@+id/screenshot_dismiss_button"
-        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
-        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
-        android:elevation="7dp"
-        android:visibility="gone"
-        app:layout_constraintStart_toEndOf="@id/screenshot_preview"
-        app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
-        app:layout_constraintTop_toTopOf="@id/screenshot_preview"
-        app:layout_constraintBottom_toTopOf="@id/screenshot_preview"
-        android:contentDescription="@string/screenshot_dismiss_description">
+                android:layout_marginVertical="@dimen/overlay_action_container_padding_vertical"
+                android:layout_marginHorizontal="@dimen/overlay_action_chip_margin_start"
+                android:background="@drawable/shelf_action_container_clipping_shape"
+                android:clipToOutline="true"
+                android:scrollbars="none">
+                <LinearLayout
+                    android:id="@+id/screenshot_actions"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:showDividers="middle"
+                    android:divider="@drawable/shelf_action_chip_divider"
+                    android:animateLayoutChanges="true"
+                    />
+            </HorizontalScrollView>
+        </FrameLayout>
+        <View
+            android:id="@+id/screenshot_preview_border"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+            android:layout_marginTop="@dimen/overlay_border_width_neg"
+            android:layout_marginEnd="@dimen/overlay_border_width_neg"
+            android:layout_marginBottom="@dimen/screenshot_shelf_vertical_margin"
+            android:elevation="4dp"
+            android:background="@drawable/overlay_border"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+            app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
+            app:layout_constraintBottom_toTopOf="@id/actions_container_background"/>
         <ImageView
-            android:id="@+id/screenshot_dismiss_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_margin="@dimen/overlay_dismiss_button_margin"
-            android:background="@drawable/circular_background"
-            android:backgroundTint="?androidprv:attr/materialColorPrimary"
-            android:tint="?androidprv:attr/materialColorOnPrimary"
-            android:padding="4dp"
-            android:src="@drawable/ic_close"/>
-    </FrameLayout>
-    <ImageView
-        android:id="@+id/screenshot_scrollable_preview"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:scaleType="matrix"
-        android:visibility="gone"
-        app:layout_constraintStart_toStartOf="@id/screenshot_preview"
-        app:layout_constraintTop_toTopOf="@id/screenshot_preview"
-        android:elevation="7dp"/>
+            android:id="@+id/screenshot_preview"
+            android:layout_width="@dimen/overlay_x_scale"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/overlay_border_width"
+            android:layout_marginBottom="@dimen/overlay_border_width"
+            android:layout_gravity="center"
+            android:elevation="4dp"
+            android:contentDescription="@string/screenshot_edit_description"
+            android:scaleType="fitEnd"
+            android:background="@drawable/overlay_preview_background"
+            android:adjustViewBounds="true"
+            android:clickable="true"
+            app:layout_constraintStart_toStartOf="@id/screenshot_preview_border"
+            app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"/>
+        <ImageView
+            android:id="@+id/screenshot_badge"
+            android:layout_width="56dp"
+            android:layout_height="56dp"
+            android:visibility="gone"
+            android:elevation="5dp"
+            app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
+            app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"/>
+        <FrameLayout
+            android:id="@+id/screenshot_dismiss_button"
+            android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+            android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+            android:elevation="7dp"
+            android:visibility="gone"
+            app:layout_constraintStart_toEndOf="@id/screenshot_preview"
+            app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
+            app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+            app:layout_constraintBottom_toTopOf="@id/screenshot_preview"
+            android:contentDescription="@string/screenshot_dismiss_description">
+            <ImageView
+                android:id="@+id/screenshot_dismiss_image"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_margin="@dimen/overlay_dismiss_button_margin"
+                android:background="@drawable/circular_background"
+                android:backgroundTint="?androidprv:attr/materialColorPrimary"
+                android:tint="?androidprv:attr/materialColorOnPrimary"
+                android:padding="4dp"
+                android:src="@drawable/ic_close"/>
+        </FrameLayout>
+        <ImageView
+            android:id="@+id/screenshot_scrollable_preview"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:scaleType="matrix"
+            android:visibility="gone"
+            app:layout_constraintStart_toStartOf="@id/screenshot_preview"
+            app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+            android:elevation="7dp"/>
 
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/guideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_end="0dp" />
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            app:layout_constraintGuide_end="0dp" />
 
-    <FrameLayout
-        android:id="@+id/screenshot_message_container"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
-        android:layout_marginTop="4dp"
-        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
-        android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
-        android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
-        android:elevation="4dp"
-        android:background="@drawable/action_chip_container_background"
-        android:visibility="gone"
-        app:layout_constraintTop_toBottomOf="@id/guideline"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintWidth_max="450dp"
-        app:layout_constraintHorizontal_bias="0">
-        <include layout="@layout/screenshot_work_profile_first_run" />
-        <include layout="@layout/screenshot_detection_notice" />
-    </FrameLayout>
+        <FrameLayout
+            android:id="@+id/screenshot_message_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
+            android:layout_marginTop="4dp"
+            android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+            android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
+            android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+            android:elevation="4dp"
+            android:background="@drawable/action_chip_container_background"
+            android:visibility="gone"
+            app:layout_constraintTop_toBottomOf="@id/guideline"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintWidth_max="450dp"
+            app:layout_constraintHorizontal_bias="0">
+            <include layout="@layout/screenshot_work_profile_first_run" />
+            <include layout="@layout/screenshot_detection_notice" />
+        </FrameLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
     <ImageView
         android:id="@+id/screenshot_flash"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index f35de05..dc9c4f1 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -52,7 +52,6 @@
                 android:paddingRight="0dp"
                 android:paddingStart="0dp"
                 android:paddingEnd="0dp"
-                android:clickable="true"
                 android:layout_width="@dimen/volume_row_slider_height"
                 android:layout_height="match_parent"
                 android:layout_gravity="center"
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 a211147..da56951 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
@@ -112,10 +112,6 @@
             }
 
         // set selected to enable marquee unless a screen reader is enabled
-        logoView.isSelected =
-            !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
-        logoDescriptionView.isSelected =
-            !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
         titleView.isSelected =
             !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
         subtitleView.isSelected =
@@ -419,19 +415,6 @@
                         indicatorMessageView.isSelected =
                             !accessibilityManager.isEnabled ||
                                 !accessibilityManager.isTouchExplorationEnabled
-
-                        /**
-                         * Note: Talkback 14.0 has new rate-limitation design to reduce frequency of
-                         * TYPE_WINDOW_CONTENT_CHANGED events to once every 30 seconds. (context:
-                         * b/281765653#comment18) Using {@link View#announceForAccessibility}
-                         * instead as workaround since sending events exceeding this frequency is
-                         * required.
-                         */
-                        indicatorMessageView?.text?.let {
-                            if (it.isNotBlank()) {
-                                view.announceForAccessibility(it)
-                            }
-                        }
                     }
                 }
 
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 511bdc4..a081ecc 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
@@ -37,8 +37,8 @@
 ) {
     val currentScene: Flow<SceneKey> = communalInteractor.desiredScene
 
-    /** Whether communal hub can be focused to enable accessibility actions. */
-    val isFocusable: Flow<Boolean> = communalInteractor.isIdleOnCommunal
+    /** Whether communal hub can be focused by accessibility tools. */
+    open val isFocusable: Flow<Boolean> = MutableStateFlow(false)
 
     /** Whether widgets are currently being re-ordered. */
     open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false)
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 9dacf8c..24ea7b6 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
@@ -22,6 +22,8 @@
 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.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
@@ -54,6 +56,7 @@
 @Inject
 constructor(
     @Application private val scope: CoroutineScope,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val communalInteractor: CommunalInteractor,
     tutorialInteractor: CommunalTutorialInteractor,
     private val shadeInteractor: ShadeInteractor,
@@ -93,6 +96,18 @@
     private val _currentPopup: MutableStateFlow<PopupType?> = MutableStateFlow(null)
     override val currentPopup: Flow<PopupType?> = _currentPopup.asStateFlow()
 
+    // The widget is focusable for accessibility when the hub is fully visible and shade is not
+    // opened.
+    override val isFocusable: Flow<Boolean> =
+        combine(
+                keyguardTransitionInteractor.isFinishedInState(KeyguardState.GLANCEABLE_HUB),
+                communalInteractor.isIdleOnCommunal,
+                shadeInteractor.isAnyFullyExpanded,
+            ) { transitionedToGlanceableHub, isIdleOnCommunal, isAnyFullyExpanded ->
+                transitionedToGlanceableHub && isIdleOnCommunal && !isAnyFullyExpanded
+            }
+            .distinctUntilChanged()
+
     private val _isEnableWidgetDialogShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
     val isEnableWidgetDialogShowing: Flow<Boolean> = _isEnableWidgetDialogShowing.asStateFlow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
index 840c3a8..0fe9bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -22,8 +22,10 @@
 import android.graphics.Rect
 import android.view.View
 import android.view.ViewOutlineProvider
+import android.view.accessibility.AccessibilityNodeInfo
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
+import com.android.systemui.res.R
 
 /** AppWidgetHostView that displays in communal hub with support for rounded corners. */
 class CommunalAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView {
@@ -42,6 +44,25 @@
     init {
         enforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context)
         enforcedRectangle = Rect()
+
+        accessibilityDelegate =
+            object : AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo
+                ) {
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                    // Hint user to long press in order to enter edit mode
+                    info.addAction(
+                        AccessibilityNodeInfo.AccessibilityAction(
+                            AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+                            context.getString(
+                                R.string.accessibility_action_label_edit_widgets
+                            ).lowercase()
+                        )
+                    )
+                }
+            }
     }
 
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 0bc29a8..95bc514 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -123,6 +123,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.scrim.ScrimDrawable;
@@ -186,6 +187,7 @@
     private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
     static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
     static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
+    static final String GLOBAL_ACTION_KEY_SYSTEM_UPDATE = "system_update";
 
     // See NotificationManagerService#scheduleDurationReachedLocked
     private static final long TOAST_FADE_TIME = 333;
@@ -213,6 +215,7 @@
     private final TelecomManager mTelecomManager;
     private final MetricsLogger mMetricsLogger;
     private final UiEventLogger mUiEventLogger;
+    private final ActivityStarter mActivityStarter;
 
     // Used for RingerModeTracker
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -317,7 +320,10 @@
         GA_CLOSE_TAP_OUTSIDE(810),
 
         @UiEvent(doc = "Power menu was closed via power + volume up.")
-        GA_CLOSE_POWER_VOLUP(811);
+        GA_CLOSE_POWER_VOLUP(811),
+
+        @UiEvent(doc = "System Update button was pressed.")
+        GA_SYSTEM_UPDATE_PRESS(1716);
 
         private final int mId;
 
@@ -349,6 +355,7 @@
             @NonNull VibratorHelper vibrator,
             @Main Resources resources,
             ConfigurationController configurationController,
+            ActivityStarter activityStarter,
             UserTracker userTracker,
             KeyguardStateController keyguardStateController,
             UserManager userManager,
@@ -385,6 +392,7 @@
         mSecureSettings = secureSettings;
         mResources = resources;
         mConfigurationController = configurationController;
+        mActivityStarter = activityStarter;
         mUserTracker = userTracker;
         mUserManager = userManager;
         mTrustManager = trustManager;
@@ -659,6 +667,8 @@
                 if (shouldDisplayEmergency()) {
                     addIfShouldShowAction(tempActions, new EmergencyDialerAction());
                 }
+            } else if (GLOBAL_ACTION_KEY_SYSTEM_UPDATE.equals(actionKey)) {
+                addIfShouldShowAction(tempActions, new SystemUpdateAction());
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
@@ -1145,6 +1155,40 @@
         }
     }
 
+    @VisibleForTesting
+    final class SystemUpdateAction extends SinglePressAction {
+
+        SystemUpdateAction() {
+            super(com.android.settingslib.R.drawable.ic_system_update,
+                    com.android.settingslib.R.string.system_update_settings_list_item_title);
+        }
+
+        @Override
+        public void onPress() {
+            mUiEventLogger.log(GlobalActionsEvent.GA_SYSTEM_UPDATE_PRESS);
+            launchSystemUpdate();
+        }
+
+        @Override
+        public boolean showDuringKeyguard() {
+            return true;
+        }
+
+        @Override
+        public boolean showBeforeProvisioning() {
+            return false;
+        }
+
+        private void launchSystemUpdate() {
+            Intent intent = new Intent(Settings.ACTION_SYSTEM_UPDATE_SETTINGS);
+            intent.addFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            // postStartActivityDismissingKeyguard is used for showing keyguard
+            // input/pin/password screen if lockscreen is secured, before sending the intent.
+            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+        }
+    }
+
     private Action getSettingsAction() {
         return new SinglePressAction(R.drawable.ic_settings,
                 R.string.global_action_settings) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 30f33a3..f8086f5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -21,19 +21,20 @@
 import android.graphics.Paint
 import android.graphics.Point
 import android.os.Handler
-import android.os.SystemClock
 import android.util.Log
 import android.util.MathUtils
 import android.view.Gravity
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import android.view.VelocityTracker
+import android.view.View
 import android.view.ViewConfiguration
 import android.view.WindowManager
 import androidx.annotation.VisibleForTesting
 import androidx.core.os.postDelayed
 import androidx.core.view.isVisible
 import androidx.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.jank.Cuj
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.dagger.qualifiers.Main
@@ -41,6 +42,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.ViewController
+import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.abs
@@ -84,6 +86,7 @@
     private val windowManager: WindowManager,
     private val viewConfiguration: ViewConfiguration,
     @Main private val mainHandler: Handler,
+    private val systemClock: SystemClock,
     private val vibratorHelper: VibratorHelper,
     private val configurationController: ConfigurationController,
     private val latencyTracker: LatencyTracker,
@@ -102,6 +105,7 @@
         private val windowManager: WindowManager,
         private val viewConfiguration: ViewConfiguration,
         @Main private val mainHandler: Handler,
+        private val systemClock: SystemClock,
         private val vibratorHelper: VibratorHelper,
         private val configurationController: ConfigurationController,
         private val latencyTracker: LatencyTracker,
@@ -115,6 +119,7 @@
                     windowManager,
                     viewConfiguration,
                     mainHandler,
+                    systemClock,
                     vibratorHelper,
                     configurationController,
                     latencyTracker,
@@ -158,9 +163,9 @@
     private var gestureInactiveTime = 0L
 
     private val elapsedTimeSinceInactive
-        get() = SystemClock.uptimeMillis() - gestureInactiveTime
+        get() = systemClock.uptimeMillis() - gestureInactiveTime
     private val elapsedTimeSinceEntry
-        get() = SystemClock.uptimeMillis() - gestureEntryTime
+        get() = systemClock.uptimeMillis() - gestureEntryTime
 
     private var pastThresholdWhileEntryOrInactiveTime = 0L
     private var entryToActiveDelay = 0F
@@ -178,7 +183,7 @@
     // Distance in pixels a drag can be considered for a fling event
     private var minFlingDistance = 0
 
-    private val failsafeRunnable = Runnable { onFailsafe() }
+    internal val failsafeRunnable = Runnable { onFailsafe() }
 
     internal enum class GestureState {
         /* Arrow is off the screen and invisible */
@@ -370,6 +375,7 @@
                 // Receiving a CANCEL implies that something else intercepted
                 // the gesture, i.e., the user did not cancel their gesture.
                 // Therefore, disappear immediately, with minimum fanfare.
+                interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
                 updateArrowState(GestureState.GONE)
                 velocityTracker = null
             }
@@ -692,10 +698,10 @@
         }
 
         if (isPastThresholdForFirstTime) {
-            pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis()
+            pastThresholdWhileEntryOrInactiveTime = systemClock.uptimeMillis()
             entryToActiveDelay = dynamicDelay()
         }
-        val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
+        val timePastThreshold = systemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
 
         return timePastThreshold > entryToActiveDelay
     }
@@ -881,6 +887,16 @@
         previousState = currentState
         currentState = newState
 
+        // First, update the jank tracker
+        when (currentState) {
+            GestureState.ENTRY -> {
+                interactionJankMonitor.cancel(Cuj.CUJ_BACK_PANEL_ARROW)
+                interactionJankMonitor.begin(mView, Cuj.CUJ_BACK_PANEL_ARROW)
+            }
+            GestureState.GONE -> interactionJankMonitor.end(Cuj.CUJ_BACK_PANEL_ARROW)
+            else -> {}
+        }
+
         when (currentState) {
             GestureState.CANCELLED -> {
                 backCallback.cancelBack()
@@ -912,7 +928,7 @@
                 mView.isVisible = true
 
                 updateRestingArrowDimens()
-                gestureEntryTime = SystemClock.uptimeMillis()
+                gestureEntryTime = systemClock.uptimeMillis()
             }
             GestureState.ACTIVE -> {
                 previousXTranslationOnActiveOffset = previousXTranslation
@@ -927,7 +943,7 @@
                 mView.popOffEdge(popVelocity)
             }
             GestureState.INACTIVE -> {
-                gestureInactiveTime = SystemClock.uptimeMillis()
+                gestureInactiveTime = systemClock.uptimeMillis()
 
                 // Typically entering INACTIVE means
                 // totalTouchDelta <= deactivationSwipeTriggerThreshold
@@ -1041,6 +1057,11 @@
         pw.println("  isLeftPanel=${mView.isLeftPanel}")
     }
 
+    @VisibleForTesting
+    internal fun getBackPanelView(): BackPanel {
+        return mView
+    }
+
     init {
         if (DEBUG)
             mView.drawDebugInfo = { canvas ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index d5b05ef..60469c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -116,8 +116,6 @@
 public class InternetDialogController implements AccessPointController.AccessPointCallback {
 
     private static final String TAG = "InternetDialogController";
-    private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
-            "android.settings.NETWORK_PROVIDER_SETTINGS";
     private static final String ACTION_WIFI_SCANNING_SETTINGS =
             "android.settings.WIFI_SCANNING_SETTINGS";
     /**
@@ -361,7 +359,8 @@
 
     @VisibleForTesting
     protected Intent getSettingsIntent() {
-        return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return new Intent(Settings.ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 2a73b53..063a52c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -16,10 +16,16 @@
 
 package com.android.systemui.scene
 
+import com.android.systemui.CoreStartable
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.startable.SceneContainerStartable
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
+import dagger.Binds
 import dagger.Module
 import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
 
 /** Scene framework Dagger module suitable for variants that want to exclude "keyguard" scenes. */
 @Module(
@@ -31,28 +37,41 @@
             ShadeSceneModule::class,
         ],
 )
-object KeyguardlessSceneContainerFrameworkModule {
+interface KeyguardlessSceneContainerFrameworkModule {
 
-    // TODO(b/298234162): provide a SceneContainerStartable without lockscreen and bouncer.
+    @Binds
+    @IntoMap
+    @ClassKey(SceneContainerStartable::class)
+    fun containerStartable(impl: SceneContainerStartable): CoreStartable
 
-    @Provides
-    fun containerConfig(): SceneContainerConfig {
-        return SceneContainerConfig(
-            // Note that this list is in z-order. The first one is the bottom-most and the
-            // last one is top-most.
-            sceneKeys =
-                listOf(
-                    Scenes.Gone,
-                    Scenes.QuickSettings,
-                    Scenes.Shade,
-                ),
-            initialSceneKey = Scenes.Gone,
-            navigationDistances =
-                mapOf(
-                    Scenes.Gone to 0,
-                    Scenes.Shade to 1,
-                    Scenes.QuickSettings to 2,
-                ),
-        )
+    @Binds
+    @IntoMap
+    @ClassKey(WindowRootViewVisibilityInteractor::class)
+    fun bindWindowRootViewVisibilityInteractor(
+        impl: WindowRootViewVisibilityInteractor
+    ): CoreStartable
+
+    companion object {
+
+        @Provides
+        fun containerConfig(): SceneContainerConfig {
+            return SceneContainerConfig(
+                // Note that this list is in z-order. The first one is the bottom-most and the
+                // last one is top-most.
+                sceneKeys =
+                    listOf(
+                        Scenes.Gone,
+                        Scenes.QuickSettings,
+                        Scenes.Shade,
+                    ),
+                initialSceneKey = Scenes.Gone,
+                navigationDistances =
+                    mapOf(
+                        Scenes.Gone to 0,
+                        Scenes.Shade to 1,
+                        Scenes.QuickSettings to 2,
+                    ),
+            )
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 994b012..5748ad4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -81,14 +81,6 @@
         toScene: SceneKey,
         transitionKey: TransitionKey? = null,
     ) {
-        check(allSceneKeys().contains(toScene)) {
-            """
-                Cannot set the desired scene key to "$toScene". The configuration does not
-                contain a scene with that key.
-            """
-                .trimIndent()
-        }
-
         dataSource.changeScene(
             toScene = toScene,
             transitionKey = transitionKey,
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 2ccd3b9..93cef61 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
@@ -162,6 +162,10 @@
         loggingReason: String,
         transitionKey: TransitionKey? = null,
     ) {
+        if (!repository.allSceneKeys().contains(toScene)) {
+            return
+        }
+
         check(
             toScene != Scenes.Gone || deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked
         ) {
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 39ec12f..d5de28a3 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
@@ -57,12 +57,14 @@
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.util.asIndenting
+import com.android.systemui.util.kotlin.getOrNull
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.printSection
 import com.android.systemui.util.println
 import dagger.Lazy
 import java.io.PrintWriter
+import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -107,7 +109,7 @@
     private val authenticationInteractor: Lazy<AuthenticationInteractor>,
     private val windowController: NotificationShadeWindowController,
     private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
-    private val centralSurfaces: CentralSurfaces,
+    private val centralSurfacesOptLazy: Lazy<Optional<CentralSurfaces>>,
     private val headsUpInteractor: HeadsUpNotificationInteractor,
     private val occlusionInteractor: SceneContainerOcclusionInteractor,
     private val faceUnlockInteractor: DeviceEntryFaceAuthInteractor,
@@ -115,6 +117,8 @@
     private val uiEventLogger: UiEventLogger,
     private val sceneBackInteractor: SceneBackInteractor,
 ) : CoreStartable {
+    private val centralSurfaces: CentralSurfaces?
+        get() = centralSurfacesOptLazy.get().getOrNull()
 
     override fun start() {
         if (SceneContainerFlag.isEnabled) {
@@ -542,7 +546,7 @@
                 }
                 .collect { isInteractingOrNull ->
                     isInteractingOrNull?.let { isInteracting ->
-                        centralSurfaces.setInteracting(
+                        centralSurfaces?.setInteracting(
                             StatusBarManager.WINDOW_STATUS_BAR,
                             isInteracting,
                         )
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index 864f29a..d4e711e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -229,6 +229,8 @@
         return CallbackToFutureAdapter.getFuture(
                 (completer) -> {
                     executor.execute(() -> {
+                        // save images as quickly as possible on the background thread
+                        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
                         try {
                             completer.set(task.execute());
                         } catch (ImageExportException | InterruptedException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
index afb0280..d1b08f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
@@ -35,4 +35,10 @@
     @Binds
     ScreenshotActionsProvider.Factory bindScreenshotActionsProviderFactory(
             DefaultScreenshotActionsProvider.Factory defaultScreenshotActionsProviderFactory);
+
+    /** */
+    @Provides
+    static ThumbnailObserver providesThumbnailObserver() {
+        return new ThumbnailObserver();
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 12a3daa..9b754f3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -32,6 +32,7 @@
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.log.DebugLogger.debugLog
 import com.android.systemui.res.R
@@ -56,6 +57,7 @@
     private val logger: UiEventLogger,
     private val viewModel: ScreenshotViewModel,
     private val windowManager: WindowManager,
+    private val thumbnailObserver: ThumbnailObserver,
     @Assisted private val context: Context,
     @Assisted private val displayId: Int
 ) : ScreenshotViewProxy {
@@ -85,6 +87,7 @@
             onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) },
             onDismissalCancelled = { animationController.getSwipeReturnAnimation().start() }
         )
+        view.updateInsets(windowManager.currentWindowMetrics.windowInsets)
         addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
         setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
         debugLog(DEBUG_WINDOW) { "adding OnComputeInternalInsetsListener" }
@@ -99,6 +102,10 @@
             info.touchableRegion.set(touchableRegion)
         }
         screenshotPreview = view.screenshotPreview
+        thumbnailObserver.setViews(
+            view.screenshotPreview,
+            view.requireViewById(R.id.screenshot_preview_border)
+        )
     }
 
     override fun reset() {
@@ -106,13 +113,19 @@
         isPendingSharedTransition = false
         viewModel.reset()
     }
-    override fun updateInsets(insets: WindowInsets) {}
+    override fun updateInsets(insets: WindowInsets) {
+        view.updateInsets(insets)
+    }
     override fun updateOrientation(insets: WindowInsets) {}
 
     override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator {
         val entrance = animationController.getEntranceAnimation(screenRect, showFlash)
-        // reset the timeout when animation finishes
-        entrance.doOnEnd { callbacks?.onUserInteraction() }
+        entrance.doOnStart { thumbnailObserver.onEntranceStarted() }
+        entrance.doOnEnd {
+            // reset the timeout when animation finishes
+            callbacks?.onUserInteraction()
+            thumbnailObserver.onEntranceComplete()
+        }
         return entrance
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ThumbnailObserver.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ThumbnailObserver.kt
new file mode 100644
index 0000000..cf62a14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ThumbnailObserver.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.screenshot
+
+import android.view.View
+import android.widget.ImageView
+
+/** An observer of thumbnail UI and entrance state that can be overridden if needed. */
+open class ThumbnailObserver {
+    /** Thumbnail image and border views. */
+    open fun setViews(image: ImageView, border: View) {}
+
+    /** Entrance animation has begun. */
+    open fun onEntranceStarted() {}
+
+    /** Entrance animation has completed/stopped. */
+    open fun onEntranceComplete() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
index 2e4473e..4eceb17 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -25,6 +25,7 @@
 import android.util.MathUtils
 import android.view.View
 import android.view.animation.AnimationUtils
+import android.widget.ImageView
 import androidx.core.animation.doOnEnd
 import androidx.core.animation.doOnStart
 import com.android.systemui.res.R
@@ -34,7 +35,7 @@
 
 class ScreenshotAnimationController(private val view: ScreenshotShelfView) {
     private var animator: Animator? = null
-    private val screenshotPreview = view.requireViewById<View>(R.id.screenshot_preview)
+    private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview)
     private val flashView = view.requireViewById<View>(R.id.screenshot_flash)
     private val actionContainer = view.requireViewById<View>(R.id.actions_container_background)
     private val fastOutSlowIn =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index f9af4b9..4437bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -17,20 +17,25 @@
 package com.android.systemui.screenshot.ui
 
 import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Insets
 import android.graphics.Rect
 import android.graphics.Region
 import android.util.AttributeSet
 import android.view.MotionEvent
 import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.widget.FrameLayout
 import android.widget.ImageView
-import androidx.constraintlayout.widget.ConstraintLayout
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.FloatingWindowUtil
+import kotlin.math.max
 
 class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
-    ConstraintLayout(context, attrs) {
+    FrameLayout(context, attrs) {
     lateinit var screenshotPreview: ImageView
+    private lateinit var screenshotStatic: ViewGroup
     var onTouchInterceptListener: ((MotionEvent) -> Boolean)? = null
 
     private val displayMetrics = context.resources.displayMetrics
@@ -43,6 +48,7 @@
         // Get focus so that the key events go to the layout.
         isFocusableInTouchMode = true
         screenshotPreview = requireViewById(R.id.screenshot_preview)
+        screenshotStatic = requireViewById(R.id.screenshot_static)
         actionsContainerBackground = requireViewById(R.id.actions_container_background)
         dismissButton = requireViewById(R.id.screenshot_dismiss_button)
     }
@@ -66,6 +72,40 @@
         return region
     }
 
+    fun updateInsets(insets: WindowInsets) {
+        val orientation = mContext.resources.configuration.orientation
+        val inPortrait = orientation == Configuration.ORIENTATION_PORTRAIT
+        val p = screenshotStatic.layoutParams as LayoutParams
+        val cutout = insets.displayCutout
+        val navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars())
+        if (cutout == null) {
+            p.setMargins(0, 0, 0, navBarInsets.bottom)
+        } else {
+            val waterfall = cutout.waterfallInsets
+            if (inPortrait) {
+                p.setMargins(
+                    waterfall.left,
+                    max(cutout.safeInsetTop.toDouble(), waterfall.top.toDouble()).toInt(),
+                    waterfall.right,
+                    max(
+                            cutout.safeInsetBottom.toDouble(),
+                            max(navBarInsets.bottom.toDouble(), waterfall.bottom.toDouble())
+                        )
+                        .toInt()
+                )
+            } else {
+                p.setMargins(
+                    max(cutout.safeInsetLeft.toDouble(), waterfall.left.toDouble()).toInt(),
+                    waterfall.top,
+                    max(cutout.safeInsetRight.toDouble(), waterfall.right.toDouble()).toInt(),
+                    max(navBarInsets.bottom.toDouble(), waterfall.bottom.toDouble()).toInt()
+                )
+            }
+        }
+        screenshotStatic.layoutParams = p
+        screenshotStatic.requestLayout()
+    }
+
     private fun getSwipeRegion(): Region {
         val swipeRegion = Region()
         val padding = FloatingWindowUtil.dpToPx(displayMetrics, -1 * TOUCH_PADDING_DP).toInt()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 3376b8c..734a530 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -16,8 +16,11 @@
 
 package com.android.systemui.screenshot.ui.binder
 
+import android.graphics.Bitmap
 import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
 import androidx.lifecycle.Lifecycle
@@ -68,7 +71,7 @@
                     launch {
                         viewModel.preview.collect { bitmap ->
                             if (bitmap != null) {
-                                previewView.setImageBitmap(bitmap)
+                                setScreenshotBitmap(previewView, bitmap)
                                 previewView.visibility = View.VISIBLE
                                 previewBorder.visibility = View.VISIBLE
                             } else {
@@ -128,4 +131,23 @@
             }
         }
     }
+
+    private fun setScreenshotBitmap(screenshotPreview: ImageView, bitmap: Bitmap) {
+        screenshotPreview.setImageBitmap(bitmap)
+        val hasPortraitAspectRatio = bitmap.width < bitmap.height
+        val fixedSize = screenshotPreview.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
+        val params: ViewGroup.LayoutParams = screenshotPreview.layoutParams
+        if (hasPortraitAspectRatio) {
+            params.width = fixedSize
+            params.height = FrameLayout.LayoutParams.WRAP_CONTENT
+            screenshotPreview.scaleType = ImageView.ScaleType.FIT_START
+        } else {
+            params.width = FrameLayout.LayoutParams.WRAP_CONTENT
+            params.height = fixedSize
+            screenshotPreview.scaleType = ImageView.ScaleType.FIT_END
+        }
+
+        screenshotPreview.layoutParams = params
+        screenshotPreview.requestLayout()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/DualShade.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/DualShade.kt
new file mode 100644
index 0000000..4db4058
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/DualShade.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.shade.shared.flag
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading and using the Dual Shade feature flag. */
+object DualShade {
+
+    /** The aconfig flag name. */
+    const val FLAG_NAME = Flags.FLAG_DUAL_SHADE
+
+    /** The flag description -- not an aconfig flag name. */
+    const val DESCRIPTION = "DualShadeFlag"
+
+    /** A token used for dependency declaration. */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Whether the feature is enabled. */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.dualShade()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, DESCRIPTION)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, DESCRIPTION)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index 0dcbe9b2..229bdce 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -26,8 +26,9 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
 
-import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.util.annotations.WeaklyReferencedCallback;
@@ -118,7 +119,7 @@
     private final Intent mServiceIntent;
     private final UserTracker mUserTracker;
     private final int mFlags;
-    private final Executor mExecutor;
+    private final Executor mBgExecutor;
     private final ServiceTransformer<T> mTransformer;
     private final ArrayList<WeakReference<Callback<T>>> mCallbacks;
     private Optional<Integer> mLastDisconnectReason;
@@ -130,30 +131,34 @@
      * Default constructor for {@link ObservableServiceConnection}.
      * @param context       The context from which the service will be bound with.
      * @param serviceIntent The intent to  bind service with.
-     * @param executor      The executor for connection callbacks to be delivered on
+     * @param bgExecutor    The executor for connection callbacks to be delivered on
      * @param transformer   A {@link ServiceTransformer} for transforming the resulting service
      *                      into a desired type.
      */
     @Inject
     public ObservableServiceConnection(Context context, Intent serviceIntent,
             UserTracker userTracker,
-            @Main Executor executor,
+            @Background Executor bgExecutor,
             ServiceTransformer<T> transformer) {
         mContext = context;
         mServiceIntent = serviceIntent;
         mUserTracker = userTracker;
         mFlags = Context.BIND_AUTO_CREATE;
-        mExecutor = executor;
+        mBgExecutor = bgExecutor;
         mTransformer = transformer;
         mCallbacks = new ArrayList<>();
         mLastDisconnectReason = Optional.empty();
     }
 
     /**
-     * Initiate binding to the service.
-     * @return {@code true} if initiating binding succeed, {@code false} otherwise.
+     * Initiate binding to the service in the background.
      */
-    public boolean bind() {
+    public void bind() {
+        mBgExecutor.execute(this::bindInternal);
+    }
+
+    @WorkerThread
+    private void bindInternal() {
         boolean bindResult = false;
         try {
             bindResult = mContext.bindServiceAsUser(mServiceIntent, this, mFlags,
@@ -166,18 +171,17 @@
         if (DEBUG) {
             Log.d(TAG, "bind. bound:" + bindResult);
         }
-        return bindResult;
     }
 
     /**
      * Disconnect from the service if bound.
      */
     public void unbind() {
-        onDisconnected(DISCONNECT_REASON_UNBIND);
+        mBgExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_UNBIND));
     }
 
     /**
-     * Adds a callback for receiving connection updates.
+     * Adds a callback for receiving connection updates. The callback is executed in the background.
      * @param callback The {@link Callback} to receive future updates.
      */
     public void addCallback(Callback<T> callback) {
@@ -185,7 +189,7 @@
             Log.d(TAG, "addCallback:" + callback);
         }
 
-        mExecutor.execute(() -> {
+        mBgExecutor.execute(() -> {
             final Iterator<WeakReference<Callback<T>>> iterator = mCallbacks.iterator();
 
             while (iterator.hasNext()) {
@@ -210,14 +214,15 @@
      * Removes previously added callback from receiving future connection updates.
      * @param callback The {@link Callback} to be removed.
      */
-    public void removeCallback(Callback callback) {
+    public void removeCallback(Callback<T> callback) {
         if (DEBUG) {
             Log.d(TAG, "removeCallback:" + callback);
         }
 
-        mExecutor.execute(() -> mCallbacks.removeIf(el-> el.get() == callback));
+        mBgExecutor.execute(() -> mCallbacks.removeIf(el-> el.get() == callback));
     }
 
+    @WorkerThread
     private void onDisconnected(@DisconnectReason int reason) {
         if (DEBUG) {
             Log.d(TAG, "onDisconnected:" + reason);
@@ -240,7 +245,7 @@
 
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
-        mExecutor.execute(() -> {
+        mBgExecutor.execute(() -> {
             if (DEBUG) {
                 Log.d(TAG, "onServiceConnected");
             }
@@ -268,7 +273,7 @@
         final Iterator<WeakReference<Callback<T>>> iterator = mCallbacks.iterator();
 
         while (iterator.hasNext()) {
-            final Callback cb = iterator.next().get();
+            final Callback<T> cb = iterator.next().get();
             if (cb != null) {
                 applicator.accept(cb);
             } else {
@@ -279,16 +284,16 @@
 
     @Override
     public void onServiceDisconnected(ComponentName name) {
-        mExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_DISCONNECTED));
+        mBgExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_DISCONNECTED));
     }
 
     @Override
     public void onBindingDied(ComponentName name) {
-        mExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_BINDING_DIED));
+        mBgExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_BINDING_DIED));
     }
 
     @Override
     public void onNullBinding(ComponentName name) {
-        mExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_NULL_BINDING));
+        mBgExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_NULL_BINDING));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
index 5979f3e..64f8246 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -48,7 +48,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final SystemClock mSystemClock;
-    private final DelayableExecutor mMainExecutor;
+    private final DelayableExecutor mBgExecutor;
     private final int mBaseReconnectDelayMs;
     private final int mMaxReconnectAttempts;
     private final int mMinConnectionDuration;
@@ -71,8 +71,8 @@
 
     private final Observer.Callback mObserverCallback = () -> initiateConnectionAttempt();
 
-    private final ObservableServiceConnection.Callback mConnectionCallback =
-            new ObservableServiceConnection.Callback() {
+    private final ObservableServiceConnection.Callback<T> mConnectionCallback =
+            new ObservableServiceConnection.Callback<>() {
         private long mStartTime;
 
         @Override
@@ -95,12 +95,10 @@
         }
     };
 
-    // TODO: b/326449074 - Ensure the DelayableExecutor is on the correct thread, and update the
-    //                     qualifier (to @Main) or name (to bgExecutor) to be consistent with that.
     @Inject
     public PersistentConnectionManager(
             SystemClock clock,
-            @Background DelayableExecutor mainExecutor,
+            @Background DelayableExecutor bgExecutor,
             DumpManager dumpManager,
             @Named(DUMPSYS_NAME) String dumpsysName,
             @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
@@ -109,7 +107,7 @@
             @Named(MIN_CONNECTION_DURATION_MS) int minConnectionDurationMs,
             @Named(OBSERVER) Observer observer) {
         mSystemClock = clock;
-        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mConnection = serviceConnection;
         mObserver = observer;
         mDumpManager = dumpManager;
@@ -195,7 +193,7 @@
                     "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
         }
 
-        mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
+        mCurrentReconnectCancelable = mBgExecutor.executeDelayed(mConnectRunnable,
                 reconnectDelayMs);
 
         mReconnectAttempts++;
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 ee642a6..0386338 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
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.media.AudioManager
+import android.util.Log
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
 import com.android.settingslib.volume.shared.model.AudioStream
@@ -144,6 +145,7 @@
             if (isMutedOrNoVolume) {
                 when (audioStream.value) {
                     AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_off
+                    AudioManager.STREAM_BLUETOOTH_SCO -> R.drawable.ic_volume_off
                     AudioManager.STREAM_VOICE_CALL -> R.drawable.ic_volume_off
                     AudioManager.STREAM_RING ->
                         if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
@@ -158,12 +160,18 @@
                             R.drawable.ic_volume_off
                         }
                     AudioManager.STREAM_ALARM -> R.drawable.ic_volume_off
-                    else -> null
+                    else -> {
+                        Log.wtf(TAG, "No icon for the stream: $audioStream")
+                        R.drawable.ic_volume_off
+                    }
                 }
             } else {
                 iconsByStream[audioStream]
+                    ?: run {
+                        Log.wtf(TAG, "No icon for the stream: $audioStream")
+                        R.drawable.ic_music_note
+                    }
             }
-                ?: error("No icon for the stream: $audioStream")
         return Icon.Resource(iconRes, null)
     }
 
@@ -196,4 +204,8 @@
      * when using [AudioStream] directly because it expects another type.
      */
     class FactoryAudioStreamWrapper(val audioStream: AudioStream)
+
+    private companion object {
+        const val TAG = "AudioStreamSliderViewModel"
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 77b3040..cfe37ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -67,6 +67,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
@@ -114,6 +115,7 @@
     private SecureSettings mSecureSettings;
     @Mock private Resources mResources;
     @Mock private ConfigurationController mConfigurationController;
+    @Mock private ActivityStarter mActivityStarter;
     @Mock private UserTracker mUserTracker;
     @Mock private KeyguardStateController mKeyguardStateController;
     @Mock private UserManager mUserManager;
@@ -173,6 +175,7 @@
                 mVibratorHelper,
                 mResources,
                 mConfigurationController,
+                mActivityStarter,
                 mUserTracker,
                 mKeyguardStateController,
                 mUserManager,
@@ -458,6 +461,18 @@
         }
     }
 
+    private static <T> void assertNoItemsOfType(List<T> stuff, Class<? extends T> klass) {
+        for (int i = 0; i < stuff.size(); i++) {
+            assertThat(stuff.get(i)).isNotInstanceOf(klass);
+        }
+    }
+
+    private static <T> void assertOneItemOfType(List<T> stuff, Class<? extends T> klass) {
+        List<?> classes = stuff.stream().map((item) -> item.getClass()).toList();
+        assertThat(classes).containsNoDuplicates();
+        assertThat(classes).contains(klass);
+    }
+
     @Test
     public void testCreateActionItems_lockdownEnabled_doesShowLockdown() {
         mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
@@ -641,6 +656,113 @@
         assertThat(mInteractor.isVisible().getValue()).isFalse();
     }
 
+    @Test
+    public void testShouldLogSystemUpdatePress() {
+        GlobalActionsDialogLite.SystemUpdateAction systemUpdateAction =
+                mGlobalActionsDialogLite.new SystemUpdateAction();
+        systemUpdateAction.onPress();
+        verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SYSTEM_UPDATE_PRESS);
+    }
+
+    @Test
+    public void testCreateActionItems_systemUpdateEnabled_doesShowSystemUpdate() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_SYSTEM_UPDATE
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+        mGlobalActionsDialogLite.createActionItems();
+
+        assertItemsOfType(mGlobalActionsDialogLite.mItems,
+                GlobalActionsDialogLite.EmergencyAction.class,
+                GlobalActionsDialogLite.LockDownAction.class,
+                GlobalActionsDialogLite.ShutDownAction.class,
+                GlobalActionsDialogLite.RestartAction.class,
+                GlobalActionsDialogLite.SystemUpdateAction.class);
+        assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
+        assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
+    }
+
+    @Test
+    public void testCreateActionItems_systemUpdateDisabled_doesntShowSystemUpdateAction() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+        mGlobalActionsDialogLite.createActionItems();
+
+        assertNoItemsOfType(mGlobalActionsDialogLite.mItems,
+                GlobalActionsDialogLite.SystemUpdateAction.class);
+        assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
+        assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
+    }
+
+    @Test
+    public void testCreateActionItems_systemUpdateEnabled_locked_showsSystemUpdate() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_SYSTEM_UPDATE
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+
+        // Show dialog with keyguard showing
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null);
+
+        assertOneItemOfType(mGlobalActionsDialogLite.mItems,
+                GlobalActionsDialogLite.SystemUpdateAction.class);
+
+        // Hide dialog
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null);
+    }
+
+    @Test
+    public void testCreateActionItems_systemUpdateEnabled_notProvisioned_noSystemUpdate() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(5).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_SYSTEM_UPDATE
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+
+        // Show dialog with keyguard showing
+        mGlobalActionsDialogLite.showOrHideDialog(false, false, null);
+
+        assertNoItemsOfType(mGlobalActionsDialogLite.mItems,
+                GlobalActionsDialogLite.SystemUpdateAction.class);
+
+        // Hide dialog
+        mGlobalActionsDialogLite.showOrHideDialog(false, false, null);
+    }
+
     private UserInfo mockCurrentUser(int flags) {
         return new UserInfo(10, "A User", flags);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index e6c259a..f1c97dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.navigationbar.gestural
 
 import android.os.Handler
-import android.os.Looper
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.HapticFeedbackConstants
@@ -28,6 +27,7 @@
 import android.view.ViewConfiguration
 import android.view.WindowManager
 import androidx.test.filters.SmallTest
+import com.android.internal.jank.Cuj
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.jank.interactionJankMonitor
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.testKosmos
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -43,6 +44,7 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -55,6 +57,7 @@
     }
     private val kosmos = testKosmos()
     private lateinit var mBackPanelController: BackPanelController
+    private lateinit var systemClock: FakeSystemClock
     private lateinit var testableLooper: TestableLooper
     private var triggerThreshold: Float = 0.0f
     private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
@@ -69,12 +72,15 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        systemClock = FakeSystemClock()
         mBackPanelController =
             BackPanelController(
                 context,
                 windowManager,
                 ViewConfiguration.get(context),
-                Handler.createAsync(checkNotNull(Looper.myLooper())),
+                Handler.createAsync(testableLooper.looper),
+                systemClock,
                 vibratorHelper,
                 configurationController,
                 latencyTracker,
@@ -83,7 +89,6 @@
         mBackPanelController.setLayoutParams(layoutParams)
         mBackPanelController.setBackCallback(backCallback)
         mBackPanelController.setIsLeftPanel(true)
-        testableLooper = TestableLooper.get(this)
         triggerThreshold = mBackPanelController.params.staticTriggerThreshold
     }
 
@@ -103,6 +108,7 @@
 
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.GONE)
+        verify(interactionJankMonitor, never()).begin(any())
     }
 
     @Test
@@ -110,23 +116,37 @@
         startTouch()
         // Move once to cross the touch slop
         continueTouch(START_X + touchSlop.toFloat() + 1)
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.ENTRY)
+        verify(interactionJankMonitor).cancel(Cuj.CUJ_BACK_PANEL_ARROW)
+        verify(interactionJankMonitor)
+            .begin(mBackPanelController.getBackPanelView(), Cuj.CUJ_BACK_PANEL_ARROW)
         // Move again to cross the back trigger threshold
         continueTouch(START_X + touchSlop + triggerThreshold + 1)
         // Wait threshold duration and hold touch past trigger threshold
-        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+        moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
         continueTouch(START_X + touchSlop + triggerThreshold + 1)
 
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.ACTIVE)
         verify(backCallback).setTriggerBack(true)
-        testableLooper.moveTimeForward(100)
-        testableLooper.processAllMessages()
+        moveTimeForward(100)
         verify(vibratorHelper)
             .performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE))
         finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.COMMITTED)
         verify(backCallback).triggerBack()
+
+        // Because the Handler that is typically used for transitioning the arrow state from
+        // COMMITTED to GONE is used as an animation-end-listener on a SpringAnimation,
+        // there is no way to meaningfully test that the state becomes GONE and that the tracked
+        // jank interaction is ended. So instead, manually trigger the failsafe, which does
+        // the same thing:
+        mBackPanelController.failsafeRunnable.run()
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.GONE)
+        verify(interactionJankMonitor).end(Cuj.CUJ_BACK_PANEL_ARROW)
     }
 
     @Test
@@ -134,19 +154,22 @@
         startTouch()
         // Move once to cross the touch slop
         continueTouch(START_X + touchSlop.toFloat() + 1)
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.ENTRY)
         // Move again to cross the back trigger threshold
         continueTouch(
             START_X + touchSlop + triggerThreshold -
                 mBackPanelController.params.deactivationTriggerThreshold
         )
         // Wait threshold duration and hold touch before trigger threshold
-        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+        moveTimeForward((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
         continueTouch(
             START_X + touchSlop + triggerThreshold -
                 mBackPanelController.params.deactivationTriggerThreshold
         )
         clearInvocations(backCallback)
-        Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
+        moveTimeForward(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
+
         // Move in the opposite direction to cross the deactivation threshold and cancel back
         continueTouch(START_X)
 
@@ -175,4 +198,10 @@
     private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
         return MotionEvent.obtain(0L, 0L, action, x, y, 0)
     }
+
+    private fun moveTimeForward(millis: Long) {
+        systemClock.advanceTime(millis)
+        testableLooper.moveTimeForward(millis)
+        testableLooper.processAllMessages()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 5d34120..8d26c87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.util.service;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -118,6 +116,7 @@
         connection.addCallback(mCallback);
         mExecutor.runAllReady();
         connection.bind();
+        mExecutor.runAllReady();
 
         when(mTransformer.convert(eq(mBinder))).thenReturn(mResult);
 
@@ -143,8 +142,8 @@
         when(mContext.bindServiceAsUser(eq(mIntent), eq(connection), anyInt(),
                 eq(UserHandle.of(MAIN_USER_ID)))).thenReturn(true);
         connection.bind();
+        mExecutor.runAllReady();
         connection.onServiceDisconnected(mComponentName);
-
         mExecutor.runAllReady();
 
         // Ensure proper disconnect reason reported back
@@ -157,6 +156,7 @@
         clearInvocations(mContext);
         // Ensure unbind after disconnect has no effect on the connection
         connection.unbind();
+        mExecutor.runAllReady();
         verify(mContext, never()).unbindService(eq(connection));
     }
 
@@ -197,7 +197,8 @@
 
         // Verify that the exception was caught and that bind returns false, and we properly
         // unbind.
-        assertThat(connection.bind()).isFalse();
+        connection.bind();
+        mExecutor.runAllReady();
         verify(mContext).unbindService(connection);
     }
 
@@ -212,13 +213,15 @@
                 .thenThrow(new SecurityException());
 
         // Verify that bind returns false and we properly unbind.
-        assertThat(connection.bind()).isFalse();
+        connection.bind();
+        mExecutor.runAllReady();
         verify(mContext).unbindService(connection);
 
         clearInvocations(mContext);
 
         // Ensure unbind after the failed bind has no effect.
         connection.unbind();
+        mExecutor.runAllReady();
         verify(mContext, never()).unbindService(eq(connection));
     }
 }
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 2017954..56e5e29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -77,10 +77,10 @@
 import android.os.UserManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -99,46 +99,28 @@
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
 import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.scene.FakeWindowRootViewComponent;
-import com.android.systemui.scene.data.repository.SceneContainerRepository;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.logger.SceneLogger;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.LargeScreenHeaderHelper;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeWindowLogger;
-import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl;
-import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.NotificationEntryHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -154,7 +136,6 @@
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderTestUtil;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -163,13 +144,10 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
 import com.android.systemui.util.FakeEventLog;
 import com.android.systemui.util.settings.FakeGlobalSettings;
 import com.android.systemui.util.settings.SystemSettings;
@@ -207,8 +185,6 @@
 import com.android.wm.shell.taskview.TaskViewTransitions;
 import com.android.wm.shell.transition.Transitions;
 
-import kotlinx.coroutines.test.TestScope;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -227,8 +203,11 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BubblesTest extends SysuiTestCase {
     @Mock
@@ -355,11 +334,8 @@
     private Icon mAppBubbleIcon;
     @Mock
     private Display mDefaultDisplay;
-    @Mock
-    private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
 
     private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-    private final TestScope mTestScope = mKosmos.getTestScope();
     private ShadeInteractor mShadeInteractor;
     private ShellTaskOrganizer mShellTaskOrganizer;
     private TaskViewTransitions mTaskViewTransitions;
@@ -376,8 +352,16 @@
     private UserHandle mUser0;
 
     private FakeBubbleProperties mBubbleProperties;
-    private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
-    private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
+    }
+
+    public BubblesTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
 
     @Before
     public void setUp() throws Exception {
@@ -402,77 +386,14 @@
 
 
         FakeDeviceProvisioningRepository deviceProvisioningRepository =
-                new FakeDeviceProvisioningRepository();
+                mKosmos.getFakeDeviceProvisioningRepository();
         deviceProvisioningRepository.setDeviceProvisioned(true);
-        FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
-        FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
-        FakeShadeRepository shadeRepository = new FakeShadeRepository();
-        FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
-
-        PowerInteractor powerInteractor = new PowerInteractor(
-                mKosmos.getPowerRepository(),
-                mKosmos.getFalsingCollector(),
-                mock(ScreenOffAnimationController.class),
-                mStatusBarStateController);
-
-        SceneInteractor sceneInteractor = new SceneInteractor(
-                mTestScope.getBackgroundScope(),
-                new SceneContainerRepository(
-                        mTestScope.getBackgroundScope(),
-                        mKosmos.getFakeSceneContainerConfig(),
-                        mKosmos.getSceneDataSource()),
-                mock(SceneLogger.class),
-                mKosmos.getDeviceUnlockedInteractor());
-
-        KeyguardTransitionInteractor keyguardTransitionInteractor =
-                mKosmos.getKeyguardTransitionInteractor();
-        KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
-                keyguardRepository,
-                new FakeCommandQueue(),
-                powerInteractor,
-                new FakeKeyguardBouncerRepository(),
-                new ConfigurationInteractor(configurationRepository),
-                shadeRepository,
-                keyguardTransitionInteractor,
-                () -> sceneInteractor,
-                () -> mKosmos.getFromGoneTransitionInteractor(),
-                () -> mKosmos.getSharedNotificationContainerInteractor(),
-                mTestScope);
-
-        mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
-        mFromPrimaryBouncerTransitionInteractor =
-                mKosmos.getFromPrimaryBouncerTransitionInteractor();
-
-        ResourcesSplitShadeStateController splitShadeStateController =
-                new ResourcesSplitShadeStateController();
 
         DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor =
                 mock(DeviceEntryUdfpsInteractor.class);
         when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(MutableStateFlow(false));
 
-        mShadeInteractor =
-                new ShadeInteractorImpl(
-                        mTestScope.getBackgroundScope(),
-                        mKosmos.getDeviceProvisioningInteractor(),
-                        new FakeDisableFlagsRepository(),
-                        mDozeParameters,
-                        keyguardRepository,
-                        keyguardTransitionInteractor,
-                        powerInteractor,
-                        new FakeUserSetupRepository(),
-                        mock(UserSwitcherInteractor.class),
-                        new ShadeInteractorLegacyImpl(
-                                mTestScope.getBackgroundScope(), keyguardRepository,
-                                new SharedNotificationContainerInteractor(
-                                        configurationRepository,
-                                        mContext,
-                                        splitShadeStateController,
-                                        keyguardInteractor,
-                                        deviceEntryUdfpsInteractor,
-                                        () -> mLargeScreenHeaderHelper),
-                                shadeRepository
-                        )
-                );
+        mShadeInteractor = mKosmos.getShadeInteractor();
 
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
                 mContext,
@@ -2467,6 +2388,10 @@
             mStateChangeCalls++;
             mLastUpdate = update;
         }
+
+        @Override
+        public void animateBubbleBarLocation(BubbleBarLocation location) {
+        }
     }
 
     private static class FakeBubbleProperties implements BubbleProperties {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 16d08dd..fff3b14 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -32,7 +32,7 @@
 val Kosmos.scenes by Fixture { fakeScenes }
 
 val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
-val Kosmos.sceneContainerConfig by Fixture {
+var Kosmos.sceneContainerConfig by Fixture {
     val navigationDistances =
         mapOf(
             Scenes.Gone to 0,
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 c0f5039..ebe591b 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
@@ -37,7 +37,7 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notificationShadeWindowController
-import com.android.systemui.statusbar.phone.centralSurfaces
+import com.android.systemui.statusbar.phone.centralSurfacesOptional
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 
 val Kosmos.sceneContainerStartable by Fixture {
@@ -58,7 +58,7 @@
         authenticationInteractor = { authenticationInteractor },
         windowController = notificationShadeWindowController,
         deviceProvisioningInteractor = deviceProvisioningInteractor,
-        centralSurfaces = centralSurfaces,
+        centralSurfacesOptLazy = { centralSurfacesOptional },
         headsUpInteractor = headsUpNotificationInteractor,
         occlusionInteractor = sceneContainerOcclusionInteractor,
         faceUnlockInteractor = deviceEntryFaceAuthInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/CentralSurfacesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/CentralSurfacesKosmos.kt
index 1611f62..f71bf03 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/CentralSurfacesKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/CentralSurfacesKosmos.kt
@@ -19,5 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.util.mockito.mock
+import java.util.Optional
 
+var Kosmos.centralSurfacesOptional by Fixture { Optional.of(centralSurfaces) }
 val Kosmos.centralSurfaces by Fixture { mock<CentralSurfaces>() }
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index e77f846f..f6885e1 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -1,11 +1,18 @@
+// Keep the following two TEST_MAPPINGs in sync:
+// frameworks/base/ravenwood/TEST_MAPPING
+// frameworks/base/tools/hoststubgen/TEST_MAPPING
 {
   "presubmit": [
+    { "name": "tiny-framework-dump-test" },
+    { "name": "hoststubgentest" },
+    { "name": "hoststubgen-invoke-test" },
     {
       "name": "RavenwoodMockitoTest_device"
     },
     {
       "name": "RavenwoodBivalentTest_device"
     },
+    // The sysui tests should match vendor/unbundled_google/packages/SystemUIGoogle/TEST_MAPPING
     {
       "name": "SystemUIGoogleTests",
       "options": [
@@ -18,6 +25,19 @@
       ]
     }
   ],
+  "presubmit-large": [
+    {
+      "name": "SystemUITests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ],
   "ravenwood-presubmit": [
     {
       "name": "RavenwoodMinimumTest",
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index b5843d0..beacde2 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -39,14 +39,18 @@
     local jar=$1
     local file=$2
 
-    sed -e '1d' -e "s/^/$jar,/"  $file
+    # Use sed to remove the header + prepend the jar filename.
+    sed -e '1d' -e "s/^/$jar,/" $file
 }
 
 collect_stats() {
     local out="$1"
     {
-        echo 'Jar,PackageName,ClassName,SupportedMethods,TotalMethods'
-        dump "framework-minus-apex"  hoststubgen_framework-minus-apex_stats.csv
+        # Copy the header, with the first column appended.
+        echo -n "Jar,"
+        head -n 1 hoststubgen_framework-minus-apex_stats.csv
+
+        dump "framework-minus-apex" hoststubgen_framework-minus-apex_stats.csv
         dump "service.core"  hoststubgen_services.core_stats.csv
     } > "$out"
 
@@ -56,7 +60,10 @@
 collect_apis() {
     local out="$1"
     {
-        echo 'Jar,PackageName,ClassName,MethodName,Descriptor'
+        # Copy the header, with the first column appended.
+        echo -n "Jar,"
+        head -n 1 hoststubgen_framework-minus-apex_apis.csv
+
         dump "framework-minus-apex"  hoststubgen_framework-minus-apex_apis.csv
         dump "service.core"  hoststubgen_services.core_apis.csv
     } > "$out"
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2d1aba4..bc83a0e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2691,7 +2691,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
-                            && idMatch(r, subId, phoneId)) {
+                            && idMatchRelaxed(r, subId, phoneId)) {
                         try {
                             r.callback.onRadioPowerStateChanged(state);
                         } catch (RemoteException ex) {
@@ -4089,6 +4089,45 @@
         }
     }
 
+    /**
+     * Match the sub id or phone id of the event to the record with relaxed rules
+     *
+     * We follow the rules below:
+     * 1) If sub id of the event is invalid, phone id should be used.
+     * 2) If record's phoneId is also invalid then allow phone 0 notifications
+     * 3) The event on default sub should be notified to the records
+     * which register the default sub id.
+     * 4) Sub id should be exactly matched for all other cases.
+     * TODO: b/337878785 for longterm fix
+     */
+    boolean idMatchRelaxed(Record r, int subId, int phoneId) {
+        if (!Flags.useRelaxedIdMatch()) {
+            return idMatch(r, subId, phoneId);
+        }
+
+        if (subId < 0) {
+            // Invalid case, we need compare phoneId.
+            // If the record does not have a valid phone Id send phone 0 notifications.
+            // A record's phoneId can get invalid if there is no SIM or modem was restarting
+            // when caller registered.
+            if (r.phoneId == INVALID_SIM_SLOT_INDEX) {
+                return (phoneId == 0);
+            } else {
+                return (r.phoneId == phoneId);
+            }
+        }
+
+        if (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+            // if the registered record does not have a valid phoneId then use the phone 0
+            if (r.phoneId == INVALID_SIM_SLOT_INDEX) {
+                return (phoneId == 0);
+            }
+            return (subId == mDefaultSubId);
+        } else {
+            return (r.subId == subId);
+        }
+    }
+
     private boolean checkFineLocationAccess(Record r) {
         return checkFineLocationAccess(r, Build.VERSION_CODES.BASE);
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 23891d2..ec0d897 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3864,10 +3864,12 @@
             final long lastTopTime = sr.app.mState.getLastTopTime();
             final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
             final long nowUptime = SystemClock.uptimeMillis();
-            if (constantTimeLimit > (nowUptime - lastTopTime)) {
+            if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) {
+                // Discard any other messages for this service
+                mFGSAnrTimer.discard(sr);
+                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
                 // The app was in the TOP state after the FGS was started so its time allowance
                 // should be counted from that time since this is considered a user interaction
-                mFGSAnrTimer.discard(sr);
                 final Message msg = mAm.mHandler.obtainMessage(
                                         ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
                 mAm.mHandler.sendMessageAtTime(msg, lastTopTime + constantTimeLimit);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b703076..c6c1f98 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -155,9 +155,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
@@ -225,9 +222,18 @@
     private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000;
 
     /**
+     * Amount of time waited for
+     * {@link ActivityTaskManagerInternal.ScreenObserver#onKeyguardStateChanged} callbacks to be
+     * called after calling {@link WindowManagerService#lockDeviceNow}.
+     * Otherwise, we should throw a {@link RuntimeException} and never dismiss the
+     * {@link UserSwitchingDialog}.
+     */
+    static final int SHOW_KEYGUARD_TIMEOUT_MS = 20 * 1000;
+
+    /**
      * Amount of time waited for {@link WindowManagerService#dismissKeyguard} callbacks to be
      * called after dismissing the keyguard.
-     * Otherwise, we should move on to dismiss the dialog {@link #dismissUserSwitchDialog()}
+     * Otherwise, we should move on to dismiss the dialog {@link #dismissUserSwitchDialog}}
      * and report user switch is complete {@link #REPORT_USER_SWITCH_COMPLETE_MSG}.
      */
     private static final int DISMISS_KEYGUARD_TIMEOUT_MS = 2 * 1000;
@@ -1925,15 +1931,8 @@
                 updateProfileRelatedCaches();
                 mInjector.getWindowManager().setCurrentUser(userId);
                 mInjector.reportCurWakefulnessUsageEvent();
-                // Once the internal notion of the active user has switched, we lock the device
-                // with the option to show the user switcher on the keyguard.
                 if (userSwitchUiEnabled) {
                     mInjector.getWindowManager().setSwitchingUser(true);
-                    // Only lock if the user has a secure keyguard PIN/Pattern/Pwd
-                    if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
-                        // Make sure the device is locked before moving on with the user switch
-                        mInjector.lockDeviceNowAndWaitForKeyguardShown();
-                    }
                 }
 
             } else {
@@ -2516,32 +2515,54 @@
 
     @VisibleForTesting
     void completeUserSwitch(int oldUserId, int newUserId) {
-        final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled();
-        // serialize each conditional step
-        await(
-                // STEP 1 - If there is no challenge set, dismiss the keyguard right away
-                isUserSwitchUiEnabled && !mInjector.getKeyguardManager().isDeviceSecure(newUserId),
-                mInjector::dismissKeyguard,
-                () -> await(
-                        // STEP 2 - If user switch ui was enabled, dismiss user switch dialog
-                        isUserSwitchUiEnabled,
-                        this::dismissUserSwitchDialog,
-                        () -> {
-                            // STEP 3 - Send REPORT_USER_SWITCH_COMPLETE_MSG to broadcast
-                            // ACTION_USER_SWITCHED & call UserSwitchObservers.onUserSwitchComplete
-                            mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
-                            mHandler.sendMessage(mHandler.obtainMessage(
-                                    REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
-                        }
-                ));
+        final Runnable sendUserSwitchCompleteMessage = () -> {
+            mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
+        };
+        if (isUserSwitchUiEnabled()) {
+            if (mInjector.getKeyguardManager().isDeviceSecure(newUserId)) {
+                this.showKeyguard(() -> dismissUserSwitchDialog(sendUserSwitchCompleteMessage));
+            } else {
+                this.dismissKeyguard(() -> dismissUserSwitchDialog(sendUserSwitchCompleteMessage));
+            }
+        } else {
+            sendUserSwitchCompleteMessage.run();
+        }
     }
 
-    private void await(boolean condition, Consumer<Runnable> conditionalStep, Runnable nextStep) {
-        if (condition) {
-            conditionalStep.accept(nextStep);
-        } else {
-            nextStep.run();
-        }
+    protected void showKeyguard(Runnable runnable) {
+        runWithTimeout(mInjector::showKeyguard, SHOW_KEYGUARD_TIMEOUT_MS, runnable, () -> {
+            throw new RuntimeException(
+                    "Keyguard is not shown in " + SHOW_KEYGUARD_TIMEOUT_MS + " ms.");
+        }, "showKeyguard");
+    }
+
+    protected void dismissKeyguard(Runnable runnable) {
+        runWithTimeout(mInjector::dismissKeyguard, DISMISS_KEYGUARD_TIMEOUT_MS, runnable, runnable,
+                "dismissKeyguard");
+    }
+
+    private void runWithTimeout(Consumer<Runnable> task, int timeoutMs, Runnable onSuccess,
+            Runnable onTimeout, String traceMsg) {
+        final AtomicInteger state = new AtomicInteger(0); // state = 0 (RUNNING)
+
+        asyncTraceBegin(traceMsg, 0);
+
+        mHandler.postDelayed(() -> {
+            if (state.compareAndSet(0, 1)) { // state = 1 (TIMEOUT)
+                asyncTraceEnd(traceMsg, 0);
+                Slogf.w(TAG, "Timeout: %s did not finish in %d ms", traceMsg, timeoutMs);
+                onTimeout.run();
+            }
+        }, timeoutMs);
+
+        task.accept(() -> {
+            if (state.compareAndSet(0, 2)) { // state = 2 (SUCCESS)
+                asyncTraceEnd(traceMsg, 0);
+                onSuccess.run();
+            }
+        });
     }
 
     private void moveUserToForeground(UserState uss, int newUserId) {
@@ -3977,29 +3998,45 @@
             return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
         }
 
-        protected void dismissKeyguard(Runnable runnable) {
-            final AtomicBoolean isFirst = new AtomicBoolean(true);
-            final Runnable runOnce = () -> {
-                if (isFirst.getAndSet(false)) {
-                    runnable.run();
-                }
-            };
+        protected void showKeyguard(Runnable runnable) {
+            if (getWindowManager().isKeyguardLocked()) {
+                runnable.run();
+                return;
+            }
+            getActivityTaskManagerInternal().registerScreenObserver(
+                    new ActivityTaskManagerInternal.ScreenObserver() {
+                        @Override
+                        public void onAwakeStateChanged(boolean isAwake) {
 
-            mHandler.postDelayed(runOnce, DISMISS_KEYGUARD_TIMEOUT_MS);
+                        }
+
+                        @Override
+                        public void onKeyguardStateChanged(boolean isShowing) {
+                            if (isShowing) {
+                                getActivityTaskManagerInternal().unregisterScreenObserver(this);
+                                runnable.run();
+                            }
+                        }
+                    }
+            );
+            getWindowManager().lockDeviceNow();
+        }
+
+        protected void dismissKeyguard(Runnable runnable) {
             getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() {
                 @Override
                 public void onDismissError() throws RemoteException {
-                    mHandler.post(runOnce);
+                    runnable.run();
                 }
 
                 @Override
                 public void onDismissSucceeded() throws RemoteException {
-                    mHandler.post(runOnce);
+                    runnable.run();
                 }
 
                 @Override
                 public void onDismissCancelled() throws RemoteException {
-                    mHandler.post(runOnce);
+                    runnable.run();
                 }
             }, /* message= */ null);
         }
@@ -4025,43 +4062,5 @@
         void onSystemUserVisibilityChanged(boolean visible) {
             getUserManagerInternal().onSystemUserVisibilityChanged(visible);
         }
-
-        void lockDeviceNowAndWaitForKeyguardShown() {
-            if (getWindowManager().isKeyguardLocked()) {
-                return;
-            }
-
-            final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
-            t.traceBegin("lockDeviceNowAndWaitForKeyguardShown");
-
-            final CountDownLatch latch = new CountDownLatch(1);
-            ActivityTaskManagerInternal.ScreenObserver screenObserver =
-                    new ActivityTaskManagerInternal.ScreenObserver() {
-                        @Override
-                        public void onAwakeStateChanged(boolean isAwake) {
-
-                        }
-
-                        @Override
-                        public void onKeyguardStateChanged(boolean isShowing) {
-                            if (isShowing) {
-                                latch.countDown();
-                            }
-                        }
-                    };
-
-            getActivityTaskManagerInternal().registerScreenObserver(screenObserver);
-            getWindowManager().lockDeviceNow();
-            try {
-                if (!latch.await(20, TimeUnit.SECONDS)) {
-                    throw new RuntimeException("Keyguard is not shown in 20 seconds");
-                }
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            } finally {
-                getActivityTaskManagerInternal().unregisterScreenObserver(screenObserver);
-                t.traceEnd();
-            }
-        }
     }
 }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e59de6a..798aaee 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1563,19 +1563,29 @@
     private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops,
             String persistentDeviceId) {
         ArrayList<AppOpsManager.OpEntry> resOps = null;
+        boolean shouldReturnRestrictedAppOps = mContext.checkPermission(
+                Manifest.permission.GET_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid())
+                == PackageManager.PERMISSION_GRANTED;
         if (ops == null) {
             resOps = new ArrayList<>();
-            for (int j=0; j<pkgOps.size(); j++) {
+            for (int j = 0; j < pkgOps.size(); j++) {
                 Op curOp = pkgOps.valueAt(j);
+                if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
+                    continue;
+                }
                 resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
             }
         } else {
-            for (int j=0; j<ops.length; j++) {
+            for (int j = 0; j < ops.length; j++) {
                 Op curOp = pkgOps.get(ops[j]);
                 if (curOp != null) {
                     if (resOps == null) {
                         resOps = new ArrayList<>();
                     }
+                    if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
+                        continue;
+                    }
                     resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
                 }
             }
@@ -4244,10 +4254,21 @@
 
     private void verifyIncomingOp(int op) {
         if (op >= 0 && op < AppOpsManager._NUM_OP) {
-            // Enforce manage appops permission if it's a restricted read op.
+            // Enforce privileged appops permission if it's a restricted read op.
             if (opRestrictsRead(op)) {
-                mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
-                        Binder.getCallingPid(), Binder.getCallingUid(), "verifyIncomingOp");
+                if (!(mContext.checkPermission(Manifest.permission.MANAGE_APPOPS,
+                        Binder.getCallingPid(), Binder.getCallingUid())
+                        == PackageManager.PERMISSION_GRANTED || mContext.checkPermission(
+                        Manifest.permission.GET_APP_OPS_STATS,
+                        Binder.getCallingPid(), Binder.getCallingUid())
+                        == PackageManager.PERMISSION_GRANTED || mContext.checkPermission(
+                        Manifest.permission.MANAGE_APP_OPS_MODES,
+                        Binder.getCallingPid(), Binder.getCallingUid())
+                        == PackageManager.PERMISSION_GRANTED)) {
+                    throw new SecurityException("verifyIncomingOp: uid " + Binder.getCallingUid()
+                            + " does not have any of {MANAGE_APPOPS, GET_APP_OPS_STATS, "
+                            + "MANAGE_APP_OPS_MODES}");
+                }
             }
             return;
         }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 77654d4..da528a2 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1772,6 +1772,7 @@
 
         @Override
         public void handleMessage(Message msg) {
+            int muteCheckDelayMs = BTA2DP_MUTE_CHECK_DELAY_MS;
             switch (msg.what) {
                 case MSG_RESTORE_DEVICES:
                     synchronized (mSetModeLock) {
@@ -1870,7 +1871,7 @@
                             btInfo.mDevice, btInfo.mProfile, btInfo.mIsLeOutput,
                             "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
                     synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onBluetoothDeviceConfigChange(btInfo,
+                        muteCheckDelayMs += mDeviceInventory.onBluetoothDeviceConfigChange(btInfo,
                                 codecAndChanged.first, codecAndChanged.second,
                                 BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
                     }
@@ -2060,7 +2061,7 @@
             // Give some time to Bluetooth service to post a connection message
             // in case of active device switch
             if (MESSAGES_MUTE_MUSIC.contains(msg.what)) {
-                sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, BTA2DP_MUTE_CHECK_DELAY_MS);
+                sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, muteCheckDelayMs);
             }
 
             if (isMessageHandledUnderWakelock(msg.what)) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 9bdc51e..c9612ca 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -864,9 +864,25 @@
         }
     }
 
+    // Additional delay added to the music mute duration when a codec config change is executed.
+    static final int BT_CONFIG_CHANGE_MUTE_DELAY_MS = 500;
 
+    /**
+     * Handles a Bluetooth link codec configuration change communicated by the Bluetooth stack.
+     * Called when either A2DP or LE Audio codec encoding or sampling rate changes:
+     * the change is communicated to native audio policy to eventually reconfigure the audio
+     * path.
+     * Also used to notify a change in preferred mode (duplex or output) for Bluetooth profiles.
+     *
+     * @param btInfo contains all information on the Bluetooth device and profile
+     * @param codec the requested audio encoding (e.g SBC)
+     * @param codecChanged true if a codec parameter changed, false for preferred mode change
+     * @param event currently only EVENT_DEVICE_CONFIG_CHANGE
+     * @return an optional additional delay in milliseconds to add to the music mute period in
+     * case of an actual codec reconfiguration.
+     */
     @GuardedBy("mDeviceBroker.mDeviceStateLock")
-    /*package*/ void onBluetoothDeviceConfigChange(
+    /*package*/ int onBluetoothDeviceConfigChange(
             @NonNull AudioDeviceBroker.BtDeviceInfo btInfo,
             @AudioSystem.AudioFormatNativeEnumForBtCodec int codec,
             boolean codecChanged, int event) {
@@ -874,10 +890,11 @@
                 + "onBluetoothDeviceConfigChange")
                 .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
 
+        int delayMs = 0;
         final BluetoothDevice btDevice = btInfo.mDevice;
         if (btDevice == null) {
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record();
-            return;
+            return delayMs;
         }
         if (AudioService.DEBUG_DEVICES) {
             Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
@@ -899,7 +916,7 @@
                         .printSlog(EventLogger.Event.ALOGI, TAG));
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored")
                         .record();
-                return;
+                return delayMs;
             }
             final String key = DeviceInfo.makeDeviceListKey(
                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
@@ -907,7 +924,7 @@
             if (di == null) {
                 Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange");
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
-                return;
+                return delayMs;
             }
 
             mmi.set(MediaMetrics.Property.ADDRESS, address)
@@ -915,7 +932,6 @@
                     .set(MediaMetrics.Property.INDEX, volume)
                     .set(MediaMetrics.Property.NAME, di.mDeviceName);
 
-
             if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
                 if (btInfo.mProfile == BluetoothProfile.A2DP
                         || btInfo.mProfile == BluetoothProfile.LE_AUDIO
@@ -943,6 +959,7 @@
                                             + address
                                             + " codec=" + AudioSystem.audioFormatToString(codec))
                                     .printSlog(EventLogger.Event.ALOGI, TAG));
+                            delayMs = BT_CONFIG_CHANGE_MUTE_DELAY_MS;
                         }
                     }
                 }
@@ -952,6 +969,7 @@
             }
         }
         mmi.record();
+        return delayMs;
     }
 
     /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index e330ed5..030ce12 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -22,7 +22,10 @@
 import static android.media.AudioManager.ADJUST_UNMUTE;
 
 import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceVolumeManager;
 import android.media.AudioManager;
+import android.media.VolumeInfo;
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
@@ -58,8 +61,12 @@
                 return getSoundDoseValue();
             case "reset-sound-dose-timeout":
                 return resetSoundDoseTimeout();
+            case "set-ringer-mode":
+                return setRingerMode();
             case "set-volume":
                 return setVolume();
+            case "set-device-volume":
+                return setDeviceVolume();
             case "adj-mute":
                 return adjMute();
             case "adj-unmute":
@@ -95,8 +102,12 @@
         pw.println("    Returns the current sound dose value");
         pw.println("  reset-sound-dose-timeout");
         pw.println("    Resets the sound dose timeout used for momentary exposure");
+        pw.println("  set-ringer-mode NORMAL|SILENT|VIBRATE");
+        pw.println("    Sets the Ringer mode to one of NORMAL|SILENT|VIBRATE");
         pw.println("  set-volume STREAM_TYPE VOLUME_INDEX");
         pw.println("    Sets the volume for STREAM_TYPE to VOLUME_INDEX");
+        pw.println("  set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE");
+        pw.println("    Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX");
         pw.println("  adj-mute STREAM_TYPE");
         pw.println("    mutes the STREAM_TYPE");
         pw.println("  adj-unmute STREAM_TYPE");
@@ -143,6 +154,34 @@
         return 0;
     }
 
+    private int setRingerMode() {
+        String ringerModeText = getNextArg();
+        if (ringerModeText == null) {
+            getErrPrintWriter().println("Error: no ringer mode specified");
+            return 1;
+        }
+
+        final int ringerMode = getRingerMode(ringerModeText);
+        if (!AudioManager.isValidRingerMode(ringerMode)) {
+            getErrPrintWriter().println(
+                    "Error: invalid value of ringerMode, should be one of NORMAL|SILENT|VIBRATE");
+            return 1;
+        }
+
+        final AudioManager am = mService.mContext.getSystemService(AudioManager.class);
+        am.setRingerModeInternal(ringerMode);
+        return 0;
+    }
+
+    private int getRingerMode(String ringerModeText) {
+        return switch (ringerModeText) {
+            case "NORMAL" -> AudioManager.RINGER_MODE_NORMAL;
+            case "VIBRATE" -> AudioManager.RINGER_MODE_VIBRATE;
+            case "SILENT" -> AudioManager.RINGER_MODE_SILENT;
+            default -> -1;
+        };
+    }
+
     private int getIsSurroundFormatEnabled() {
         String surroundFormatText = getNextArg();
 
@@ -257,6 +296,23 @@
         return 0;
     }
 
+    private int setDeviceVolume() {
+        final Context context = mService.mContext;
+        final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService(
+                Context.AUDIO_DEVICE_VOLUME_SERVICE);
+        final int stream = readIntArg();
+        final int index = readIntArg();
+        final int device = readIntArg();
+
+        final VolumeInfo volume = new VolumeInfo.Builder(stream).setVolumeIndex(index).build();
+        final AudioDeviceAttributes ada = new AudioDeviceAttributes(
+                /*native type*/ device, /*address*/ "foo");
+        getOutPrintWriter().println(
+                "calling AudioDeviceVolumeManager.setDeviceVolume(" + volume + ", " + ada + ")");
+        advm.setDeviceVolume(volume, ada);
+        return 0;
+    }
+
     private int adjMute() {
         final Context context = mService.mContext;
         final AudioManager am = context.getSystemService(AudioManager.class);
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index 138186b..4c5a3c2 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -333,8 +333,8 @@
             out.println();
             out.println("The commands and default sources are:");
             out.println("      text <string> (Default: keyboard)");
-            out.println("      keyevent [--longpress|--doubletap|--async"
-                    + "|--delay <duration between keycodes in ms>]"
+            out.println("      keyevent [--longpress|--duration <duration to hold key down in ms>]"
+                    + " [--doubletap] [--async] [--delay <duration between keycodes in ms>]"
                     + " <key code number or name> ..."
                     + " (Default: keyboard)");
             out.println("      tap <x> <y> (Default: touchscreen)");
@@ -402,6 +402,7 @@
         boolean async = false;
         boolean doubleTap = false;
         long delayMs = 0;
+        long durationMs = 0;
 
         String arg = getNextArgRequired();
         do {
@@ -411,9 +412,21 @@
             doubleTap = (doubleTap || arg.equals("--doubletap"));
             if (arg.equals("--delay")) {
                 delayMs = Long.parseLong(getNextArgRequired());
+            } else if (arg.equals("--duration")) {
+                durationMs = Long.parseLong(getNextArgRequired());
             }
         } while ((arg = getNextArg()) != null);
 
+        if (durationMs > 0 && longPress) {
+            getErrPrintWriter().println(
+                    "--duration and --longpress cannot be used at the same time.");
+            throw new IllegalArgumentException(
+                    "keyevent args should only contain either durationMs or longPress");
+        }
+        if (longPress) {
+            durationMs = ViewConfiguration.getLongPressTimeout();
+        }
+
         boolean firstInput = true;
         do {
             if (!firstInput && delayMs > 0) {
@@ -422,16 +435,17 @@
             firstInput = false;
 
             final int keyCode = KeyEvent.keyCodeFromString(arg);
-            sendKeyEvent(inputSource, keyCode, longPress, displayId, async);
+            sendKeyEvent(inputSource, keyCode, durationMs, displayId, async);
             if (doubleTap) {
                 sleep(ViewConfiguration.getDoubleTapMinTime());
-                sendKeyEvent(inputSource, keyCode, longPress, displayId, async);
+                sendKeyEvent(inputSource, keyCode, durationMs, displayId, async);
             }
         } while ((arg = getNextArg()) != null);
     }
 
     private void sendKeyEvent(
-            int inputSource, int keyCode, boolean longPress, int displayId, boolean async) {
+            int inputSource, int keyCode, long durationMs, int displayId,
+            boolean async) {
         final long now = SystemClock.uptimeMillis();
 
         KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */,
@@ -440,13 +454,23 @@
         event.setDisplayId(displayId);
 
         injectKeyEvent(event, async);
-        if (longPress) {
-            sleep(ViewConfiguration.getLongPressTimeout());
-            // Some long press behavior would check the event time, we set a new event time here.
-            final long nextEventTime = now + ViewConfiguration.getLongPressTimeout();
-            KeyEvent longPressEvent = KeyEvent.changeTimeRepeat(
-                    event, nextEventTime, 1 /* repeatCount */, KeyEvent.FLAG_LONG_PRESS);
-            injectKeyEvent(longPressEvent, async);
+        long firstSleepDurationMs = Math.min(durationMs, ViewConfiguration.getLongPressTimeout());
+        if (firstSleepDurationMs > 0) {
+            sleep(firstSleepDurationMs);
+            // Send FLAG_LONG_PRESS right after `longPressTimeout`, and resume sleep if needed.
+            if (durationMs >= ViewConfiguration.getLongPressTimeout()) {
+                // Some long press behavior would check the event time, we set a new event time
+                // here.
+                final long nextEventTime = now + ViewConfiguration.getLongPressTimeout();
+                KeyEvent longPressEvent = KeyEvent.changeTimeRepeat(event, nextEventTime,
+                        1 /* repeatCount */, KeyEvent.FLAG_LONG_PRESS);
+                injectKeyEvent(longPressEvent, async);
+
+                long secondSleepDurationMs = durationMs - firstSleepDurationMs;
+                if (secondSleepDurationMs > 0) {
+                    sleep(secondSleepDurationMs);
+                }
+            }
         }
         injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP), async);
     }
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 8002300..880787e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -203,7 +203,7 @@
 
         SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
         if (subManager != null) {
-            if (Flags.subscriptionsListenerThread()) {
+            if (Flags.subscriptionsChangedListenerThread()) {
                 subManager.addOnSubscriptionsChangedListener(FgThread.getExecutor(),
                         mOnSubscriptionsChangeListener);
             } else {
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 4fc1a17..ad6b0ca 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "presubmit-large": [
     {
-      "name": "CtsHostsideNetworkTests",
+      "name": "CtsHostsideNetworkPolicyTests",
       "options": [
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4747689..143bc5c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1725,7 +1725,28 @@
         synchronized (mConfigLock) {
             if (policy == null || mConfig == null) return;
             final ZenModeConfig newConfig = mConfig.copy();
-            newConfig.applyNotificationPolicy(policy);
+            if (Flags.modesApi() && !Flags.modesUi()) {
+                // Fix for b/337193321 -- propagate changes to notificationPolicy to rules where
+                // the user cannot edit zen policy to emulate the previous "inheritance".
+                ZenPolicy previousPolicy = ZenAdapters.notificationPolicyToZenPolicy(
+                        newConfig.toNotificationPolicy());
+                ZenPolicy newPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+
+                newConfig.applyNotificationPolicy(policy);
+
+                if (!previousPolicy.equals(newPolicy)) {
+                    for (ZenRule rule : newConfig.automaticRules.values()) {
+                        if (!SystemZenRules.isSystemOwnedRule(rule)
+                                && rule.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                                && (rule.zenPolicy == null || rule.zenPolicy.equals(previousPolicy)
+                                        || rule.zenPolicy.equals(getDefaultZenPolicy()))) {
+                            rule.zenPolicy = newPolicy;
+                        }
+                    }
+                }
+            } else {
+                newConfig.applyNotificationPolicy(policy);
+            }
             setConfigLocked(newConfig, null, origin, "setNotificationPolicy", callingUid);
         }
     }
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index c8fd7e4..8a85328 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -19,6 +19,7 @@
 import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.app.AppOpsManager;
@@ -68,6 +69,7 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.OptionalInt;
@@ -335,14 +337,22 @@
     }
 
     static class Injector {
+        class RoleManagerWrapper {
+            List<String> getRoleHolders(@NonNull String roleName) {
+                return mContext.getSystemService(RoleManager.class).getRoleHolders(roleName);
+            }
+        }
+
         Context mContext;
         ArraySet<String> mAllowlistedPackages;
         AtomicFile mMappingFile;
+        RoleManagerWrapper mRoleManagerWrapper;
 
         Injector(Context context, ArraySet<String> allowlistedPackages, AtomicFile mappingFile) {
             mContext = context;
             mAllowlistedPackages = allowlistedPackages;
             mMappingFile = mappingFile;
+            mRoleManagerWrapper = new RoleManagerWrapper();
         }
 
         Context getContext() {
@@ -368,6 +378,10 @@
         void setSystemProperty(String key, String value) {
             SystemProperties.set(key, value);
         }
+
+        RoleManagerWrapper getRoleManagerWrapper() {
+            return mRoleManagerWrapper;
+        }
     }
 
     BugreportManagerServiceImpl(Context context) {
@@ -546,7 +560,7 @@
         if (!allowlisted) {
             final long token = Binder.clearCallingIdentity();
             try {
-                allowlisted = mContext.getSystemService(RoleManager.class).getRoleHolders(
+                allowlisted = mInjector.getRoleManagerWrapper().getRoleHolders(
                         ROLE_SYSTEM_AUTOMOTIVE_PROJECTION).contains(callingPackage);
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0f4e482..ae485ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3984,6 +3984,8 @@
 
         // packageName -> list of components to send broadcasts now
         final ArrayMap<String, ArrayList<String>> sendNowBroadcasts = new ArrayMap<>(targetSize);
+        final List<PackageMetrics.ComponentStateMetrics> componentStateMetricsList =
+                new ArrayList<PackageMetrics.ComponentStateMetrics>();
         synchronized (mLock) {
             Computer computer = snapshotComputer();
             boolean scheduleBroadcastMessage = false;
@@ -3997,11 +3999,17 @@
                 // update enabled settings
                 final ComponentEnabledSetting setting = settings.get(i);
                 final String packageName = setting.getPackageName();
-                if (!setEnabledSettingInternalLocked(computer, pkgSettings.get(packageName),
-                        setting, userId, callingPackage)) {
+                final PackageSetting packageSetting = pkgSettings.get(packageName);
+                final PackageMetrics.ComponentStateMetrics componentStateMetrics =
+                        new PackageMetrics.ComponentStateMetrics(setting,
+                                UserHandle.getUid(userId, packageSetting.getAppId()),
+                                packageSetting.getEnabled(userId));
+                if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId,
+                        callingPackage)) {
                     continue;
                 }
                 anyChanged = true;
+                componentStateMetricsList.add(componentStateMetrics);
 
                 if ((setting.getEnabledFlags() & PackageManager.SYNCHRONOUS) != 0) {
                     isSynchronous = true;
@@ -4029,6 +4037,9 @@
                 return;
             }
 
+            // Log the metrics when the component state is changed.
+            PackageMetrics.reportComponentStateChanged(computer, componentStateMetricsList, userId);
+
             if (isSynchronous) {
                 flushPackageRestrictionsAsUserInternalLocked(userId);
             } else {
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index a0b6897..20598f9 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -19,12 +19,21 @@
 import static android.os.Process.INVALID_UID;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.admin.SecurityLog;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.Flags;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Pair;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -41,12 +50,14 @@
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Metrics class for reporting stats to logging infrastructures like statsd
  */
 final class PackageMetrics {
+    private static final String TAG = "PackageMetrics";
     public static final int STEP_PREPARE = 1;
     public static final int STEP_SCAN = 2;
     public static final int STEP_RECONCILE = 3;
@@ -344,4 +355,76 @@
         SecurityLog.writeEvent(SecurityLog.TAG_PACKAGE_UNINSTALLED, packageName, versionCode,
                 userId);
     }
+
+    public static class ComponentStateMetrics {
+        public int mUid;
+        public int mComponentOldState;
+        public int mComponentNewState;
+        public boolean mIsForWholeApp;
+        @NonNull private String mPackageName;
+        @Nullable private String mClassName;
+
+        ComponentStateMetrics(@NonNull PackageManager.ComponentEnabledSetting setting, int uid,
+                int componentOldState) {
+            mUid = uid;
+            mComponentOldState = componentOldState;
+            mComponentNewState = setting.getEnabledState();
+            mIsForWholeApp = !setting.isComponent();
+            mPackageName = setting.getPackageName();
+            mClassName = setting.getClassName();
+        }
+
+        public boolean isSameComponent(ActivityInfo activityInfo) {
+            if (activityInfo == null) {
+                return false;
+            }
+            return mIsForWholeApp ? TextUtils.equals(activityInfo.packageName, mPackageName)
+                    : activityInfo.getComponentName().equals(
+                            new ComponentName(mPackageName, mClassName));
+        }
+    }
+
+    public static void reportComponentStateChanged(@NonNull Computer computer,
+            List<ComponentStateMetrics> componentStateMetricsList, @UserIdInt int userId) {
+        if (!Flags.componentStateChangedMetrics()) {
+            return;
+        }
+        if (componentStateMetricsList == null || componentStateMetricsList.isEmpty()) {
+            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.mIsForWholeApp);
+        }
+    }
+
+    private static void reportComponentStateChanged(int uid, int componentOldState,
+            int componentNewState, boolean isLauncher, boolean isForWholeApp) {
+        FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
+                uid, componentOldState, componentNewState, isLauncher, isForWholeApp);
+    }
+
+    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/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5bd845f..3303367 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2167,7 +2167,7 @@
             // We don't need to start a new activity, and the client said not to do anything
             // if that is the case, so this is it!  And for paranoia, make sure we have
             // correctly resumed the top activity.
-            if (!mMovedToFront && mDoResume && !avoidMoveToFront()) {
+            if (!mMovedToFront && mDoResume) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Bring to front target: %s from %s", mTargetRootTask,
                         targetTaskTop);
                 mTargetRootTask.moveToFront("intentActivityFound");
@@ -2196,7 +2196,7 @@
         if (mMovedToFront) {
             // We moved the task to front, use starting window to hide initial drawn delay.
             targetTaskTop.showStartingWindow(true /* taskSwitch */);
-        } else if (mDoResume && !avoidMoveToFront()) {
+        } else if (mDoResume) {
             // Make sure the root task and its belonging display are moved to topmost.
             mTargetRootTask.moveToFront("intentActivityFound");
         }
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 90a53ee..c9703d8 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -303,8 +303,7 @@
                     removedWindowContainer = currentTask;
                     backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
                     final ActivityRecord ar = prevTask.getTopNonFinishingActivity();
-                    mShowWallpaper =
-                            ar != null && ar.forAllWindows(WindowState::hasWallpaper, true);
+                    mShowWallpaper = ar != null && ar.hasWallpaper();
                 } else {
                     // If it reaches the top activity, we will check the below task from parent.
                     // If it's null or multi-window and has different parent task, fallback the type
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index d56b503..1c59977 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -111,19 +111,7 @@
         }
 
         if (phase == PHASE_WINDOWING_MODE) {
-            return RESULT_DONE;
-        }
-
-        // TODO(b/336998072) - Find a better solution to this that makes use of the logic from
-        //  TaskLaunchParamsModifier. Put logic in common utils, return RESULT_CONTINUE, inherit
-        //  from parent class, etc.
-        if (outParams.mPreferredTaskDisplayArea == null && task.getRootTask() != null) {
-            appendLog("display-from-task=" + task.getRootTask().getDisplayId());
-            outParams.mPreferredTaskDisplayArea = task.getRootTask().getDisplayArea();
-        }
-
-        if (phase == PHASE_DISPLAY_AREA) {
-            return RESULT_DONE;
+            return RESULT_CONTINUE;
         }
 
         if (!currentParams.mBounds.isEmpty()) {
@@ -135,7 +123,7 @@
 
         appendLog("setting desktop mode task bounds to %s", outParams.mBounds);
 
-        return RESULT_DONE;
+        return RESULT_CONTINUE;
     }
 
     /**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6458eac..d555f1a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -666,15 +666,6 @@
             DELEGATION_CERT_SELECTION,
     });
 
-    /**
-     * System property whose value indicates whether the device is fully owned by an organization:
-     * it can be either a device owner device, or a device with an organization-owned managed
-     * profile.
-     *
-     * <p>The state is stored as a Boolean string.
-     */
-    private static final String PROPERTY_ORGANIZATION_OWNED = "ro.organization_owned";
-
     private static final int STATUS_BAR_DISABLE_MASK =
             StatusBarManager.DISABLE_EXPAND |
             StatusBarManager.DISABLE_NOTIFICATION_ICONS |
@@ -2356,7 +2347,6 @@
     void loadOwners() {
         synchronized (getLockObject()) {
             mOwners.load();
-            setDeviceOwnershipSystemPropertyLocked();
             if (mOwners.hasDeviceOwner()) {
                 setGlobalSettingDeviceOwnerType(
                         mOwners.getDeviceOwnerType(mOwners.getDeviceOwnerPackageName()));
@@ -2720,27 +2710,6 @@
                 + defaultRestrictions);
     }
 
-    private void setDeviceOwnershipSystemPropertyLocked() {
-        final boolean deviceProvisioned =
-                mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        final boolean hasDeviceOwner = mOwners.hasDeviceOwner();
-        final boolean hasOrgOwnedProfile = isOrganizationOwnedDeviceWithManagedProfile();
-        // If the device is not provisioned and there is currently no management, do not set the
-        // read-only system property yet, since device owner / org-owned profile may still be
-        // provisioned.
-        if (!hasDeviceOwner && !hasOrgOwnedProfile && !deviceProvisioned) {
-            return;
-        }
-        final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile);
-        final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null);
-        if (TextUtils.isEmpty(currentVal)) {
-            Slogf.i(LOG_TAG, "Set ro.organization_owned property to " + value);
-            mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value);
-        } else if (!value.equals(currentVal)) {
-            Slogf.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
-        }
-    }
-
     private void maybeStartSecurityLogMonitorOnActivityManagerReady() {
         if (!mInjector.securityLogIsLoggingEnabled()) {
             return;
@@ -9447,7 +9416,6 @@
 
             mOwners.setDeviceOwner(admin, userId);
             mOwners.writeDeviceOwner();
-            setDeviceOwnershipSystemPropertyLocked();
 
             //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
             // hard-coded default value setting.
@@ -15303,8 +15271,6 @@
     private class SetupContentObserver extends ContentObserver {
         private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
                 Settings.Secure.USER_SETUP_COMPLETE);
-        private final Uri mDeviceProvisioned = Settings.Global.getUriFor(
-                Settings.Global.DEVICE_PROVISIONED);
         private final Uri mPaired = Settings.Secure.getUriFor(Settings.Secure.DEVICE_PAIRED);
         private final Uri mDefaultImeChanged = Settings.Secure.getUriFor(
                 Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -15318,7 +15284,6 @@
 
         void register() {
             mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
-            mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
             if (mIsWatch) {
                 mInjector.registerContentObserver(mPaired, false, this, UserHandle.USER_ALL);
             }
@@ -15334,12 +15299,6 @@
         public void onChange(boolean selfChange, Uri uri, int userId) {
             if (mUserSetupComplete.equals(uri) || (mIsWatch && mPaired.equals(uri))) {
                 updateUserSetupCompleteAndPaired();
-            } else if (mDeviceProvisioned.equals(uri)) {
-                synchronized (getLockObject()) {
-                    // Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property
-                    // is delayed until device is marked as provisioned.
-                    setDeviceOwnershipSystemPropertyLocked();
-                }
             } else if (mDefaultImeChanged.equals(uri)) {
                 synchronized (getLockObject()) {
                     if (mUserIdsWithPendingChangesByOwner.contains(userId)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index a0d9be54..eeb4976 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -16,6 +16,9 @@
 
 package com.android.server.devicepolicy;
 
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -185,6 +188,9 @@
         }
     }
 
+    // TODO: when a local policy exists for a user, this callback will be invoked for this user
+    // individually as well as for USER_ALL. This can be optimized by separating local and global
+    // enforcement in the policy engine.
     static boolean setUserControlDisabledPackages(
             @Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) {
         Binder.withCleanCallingIdentity(() -> {
@@ -201,20 +207,35 @@
                     return;
                 }
                 final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-                for (var pkg : packages) {
-                    final var appInfo = pmi.getApplicationInfo(pkg,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                            Process.myUid(), userId);
-                    if (appInfo != null) {
-                        DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
-                    }
-                }
+                resolveUsers(userId).forEach(
+                        user -> setBgUsageAppOp(packages, pmi, user, appOpsManager));
             }
         });
         return true;
     }
 
+    /** Handles USER_ALL expanding it into the list of all intact users. */
+    private static List<Integer> resolveUsers(int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+            return userManager.getUsers(/* excludeDying= */ true)
+                    .stream().map(ui -> ui.id).toList();
+        } else {
+            return List.of(userId);
+        }
+    }
+
+    private static void setBgUsageAppOp(Set<String> packages, PackageManagerInternal pmi,
+            int userId, AppOpsManager appOpsManager) {
+        for (var pkg : packages) {
+            int packageFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            final var appInfo = pmi.getApplicationInfo(pkg, packageFlags, Process.myUid(), userId);
+            if (appInfo != null) {
+                DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
+            }
+        }
+    }
+
     static boolean addPersistentPreferredActivity(
             @Nullable ComponentName preferredActivity, @NonNull Context context, int userId,
             @NonNull PolicyKey policyKey) {
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
index 23314cd..1322545 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
@@ -20,12 +20,17 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+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.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
 import android.os.Looper;
 import android.platform.test.annotations.EnableFlags;
 import android.service.dreams.DreamService;
@@ -41,6 +46,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -83,6 +89,18 @@
         assertThat(metadata.dreamCategory).isEqualTo(DreamService.DREAM_CATEGORY_DEFAULT);
     }
 
+    @Test
+    public void testMetadataParsing_exceptionReading() {
+        final PackageManager packageManager = Mockito.mock(PackageManager.class);
+        final ServiceInfo serviceInfo = Mockito.mock(ServiceInfo.class);
+        final TypedArray rawMetadata = Mockito.mock(TypedArray.class);
+        when(packageManager.extractPackageItemInfoAttributes(eq(serviceInfo), any(), any(), any()))
+                .thenReturn(rawMetadata);
+        when(rawMetadata.getString(anyInt())).thenThrow(new RuntimeException("failure"));
+
+        assertThat(DreamService.getDreamMetadata(packageManager, serviceInfo)).isNull();
+    }
+
     private DreamService.DreamMetadata getDreamMetadata(String dreamClassName)
             throws PackageManager.NameNotFoundException {
         final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index c6f3eb3..30e3b18 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -58,6 +58,7 @@
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
@@ -90,6 +91,7 @@
 import android.os.Message;
 import android.os.PowerManagerInternal;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
@@ -203,7 +205,10 @@
             doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
             doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
-            doNothing().when(mInjector).lockDeviceNowAndWaitForKeyguardShown();
+            doAnswer(invocation -> {
+                ((Runnable) invocation.getArgument(0)).run();
+                return null;
+            }).when(mInjector).showKeyguard(any());
             mockIsUsersOnSecondaryDisplaysEnabled(false);
             // All UserController params are set to default.
 
@@ -540,7 +545,6 @@
         expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
         if (backgroundUserStopping) {
             expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG);
-            expectedCodes.add(0); // this is for directly posting in stopping.
         }
         if (expectScheduleBackgroundUserStopping) {
             expectedCodes.add(SCHEDULED_STOP_BACKGROUND_USER_MSG);
@@ -1419,21 +1423,13 @@
         // mock the device to be secure in order to expect the keyguard to be shown
         when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
 
-        // call real lockDeviceNowAndWaitForKeyguardShown method for this test
-        doCallRealMethod().when(mInjector).lockDeviceNowAndWaitForKeyguardShown();
+        // call real showKeyguard method for this test
+        doCallRealMethod().when(mInjector).showKeyguard(any());
 
-        // call startUser on a thread because we're expecting it to be blocked
-        Thread threadStartUser = new Thread(()-> {
-            mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
-        });
-        threadStartUser.start();
+        mUserController.completeUserSwitch(TEST_USER_ID1, TEST_USER_ID2);
 
-        // make sure the switch is stalled...
-        Thread.sleep(2000);
-        // by checking REPORT_USER_SWITCH_MSG is not sent yet
-        assertNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG));
-        // and the thread is still alive
-        assertTrue(threadStartUser.isAlive());
+        // make sure the switch is stalled by checking the UserSwitchingDialog is not dismissed yet
+        verify(mInjector, never()).dismissUserSwitchingDialog(any());
 
         // mock send the keyguard shown event
         ArgumentCaptor<ActivityTaskManagerInternal.ScreenObserver> captor = ArgumentCaptor.forClass(
@@ -1441,12 +1437,42 @@
         verify(mInjector.mActivityTaskManagerInternal).registerScreenObserver(captor.capture());
         captor.getValue().onKeyguardStateChanged(true);
 
-        // verify the switch now moves on...
-        Thread.sleep(1000);
-        // by checking REPORT_USER_SWITCH_MSG is sent
-        assertNotNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG));
-        // and the thread is finished
-        assertFalse(threadStartUser.isAlive());
+        // verify the switch now moves on by checking the UserSwitchingDialog is dismissed
+        verify(mInjector, atLeastOnce()).dismissUserSwitchingDialog(any());
+
+        // verify that SHOW_KEYGUARD_TIMEOUT is ignored and does not crash the system
+        try {
+            mInjector.mHandler.processPostDelayedCallbacksWithin(
+                    UserController.SHOW_KEYGUARD_TIMEOUT_MS);
+        } catch (RuntimeException e) {
+            throw new AssertionError(
+                    "SHOW_KEYGUARD_TIMEOUT is not ignored and crashed the system", e);
+        }
+    }
+
+    @Test
+    public void testRuntimeExceptionIsThrownIfTheKeyguardIsNotShown() throws Exception {
+        // enable user switch ui, because keyguard is only shown then
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false,
+                /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+        // mock the device to be secure in order to expect the keyguard to be shown
+        when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
+
+        // suppress showKeyguard method for this test
+        doNothing().when(mInjector).showKeyguard(any());
+
+        mUserController.completeUserSwitch(TEST_USER_ID1, TEST_USER_ID2);
+
+        // verify that the system has crashed
+        assertThrows("Should have thrown RuntimeException", RuntimeException.class, () -> {
+            mInjector.mHandler.processPostDelayedCallbacksWithin(
+                    UserController.SHOW_KEYGUARD_TIMEOUT_MS);
+        });
+
+        // make sure the UserSwitchingDialog is not dismissed
+        verify(mInjector, never()).dismissUserSwitchingDialog(any());
     }
 
     private void setUpAndStartUserInBackground(int userId) throws Exception {
@@ -1793,7 +1819,9 @@
         Set<Integer> getMessageCodes() {
             Set<Integer> result = new LinkedHashSet<>();
             for (Message msg : mMessages) {
-                result.add(msg.what);
+                if (msg.what != 0) { // ignore mHandle.post and mHandler.postDelayed messages
+                    result.add(msg.what);
+                }
             }
             return result;
         }
@@ -1817,14 +1845,28 @@
 
         @Override
         public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            final Runnable cb = msg.getCallback();
+            if (cb != null && uptimeMillis <= SystemClock.uptimeMillis()) {
+                // run mHandler.post calls immediately
+                cb.run();
+                return true;
+            }
             Message copy = new Message();
             copy.copyFrom(msg);
+            copy.setCallback(cb);
             mMessages.add(copy);
-            if (msg.getCallback() != null) {
-                msg.getCallback().run();
-                msg.setCallback(null);
-            }
             return super.sendMessageAtTime(msg, uptimeMillis);
         }
+
+        public void processPostDelayedCallbacksWithin(long millis) {
+            final long whenMax = SystemClock.uptimeMillis() + millis;
+            for (Message msg : mMessages) {
+                final Runnable cb = msg.getCallback();
+                if (cb != null && msg.getWhen() <= whenMax) {
+                    msg.setCallback(null);
+                    cb.run();
+                }
+            }
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index a6f2196..9862663 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.os;
 
-import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
@@ -25,9 +23,9 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.flags.Flags;
-import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
@@ -61,6 +59,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.FileDescriptor;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -104,7 +104,7 @@
         ArraySet<String> mAllowlistedPackages = new ArraySet<>();
         mAllowlistedPackages.add(mContext.getPackageName());
         mInjector = new TestInjector(mContext, mAllowlistedPackages, mMappingFile,
-                mMockUserManager, mMockDevicePolicyManager);
+                mMockUserManager, mMockDevicePolicyManager, null);
         mService = new BugreportManagerServiceImpl(mInjector);
         mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager(mMappingFile);
         when(mPackageManager.getPackageUidAsUser(anyString(), anyInt())).thenReturn(mCallingUid);
@@ -114,24 +114,8 @@
 
     @After
     public void tearDown() throws Exception {
-        // Changes to RoleManager persist between tests, so we need to clear out any funny
-        // business we did in previous tests.
+        // Clean up the mapping file between tests since it would otherwise persist.
         mMappingFile.delete();
-        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
-        CallbackFuture future = new CallbackFuture();
-        runWithShellPermissionIdentity(
-                () -> {
-                    roleManager.setBypassingRoleQualification(false);
-                    roleManager.removeRoleHolderAsUser(
-                            "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
-                            mContext.getPackageName(),
-                            /* flags= */ 0,
-                            Process.myUserHandle(),
-                            mContext.getMainExecutor(),
-                            future);
-                });
-
-        assertThat(future.get()).isEqualTo(true);
     }
 
     @Test
@@ -267,7 +251,10 @@
 
     @Test
     public void testCancelBugreportWithoutRole() {
-        clearAllowlist();
+        // Create a new service to clear the allowlist
+        mService = new BugreportManagerServiceImpl(
+                new TestInjector(mContext, new ArraySet<>(), mMappingFile,
+                        mMockUserManager, mMockDevicePolicyManager, null));
 
         assertThrows(SecurityException.class, () -> mService.cancelBugreport(
                 Binder.getCallingUid(), mContext.getPackageName()));
@@ -275,29 +262,13 @@
 
     @Test
     public void testCancelBugreportWithRole() throws Exception {
-        clearAllowlist();
-        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
-        CallbackFuture future = new CallbackFuture();
-        runWithShellPermissionIdentity(
-                () -> {
-                    roleManager.setBypassingRoleQualification(true);
-                    roleManager.addRoleHolderAsUser(
-                            "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
-                            mContext.getPackageName(),
-                            /* flags= */ 0,
-                            Process.myUserHandle(),
-                            mContext.getMainExecutor(),
-                            future);
-                });
-
-        assertThat(future.get()).isEqualTo(true);
-        mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName());
-    }
-
-    private void clearAllowlist() {
+        // Create a new service to clear the allowlist, but override the role manager
         mService = new BugreportManagerServiceImpl(
                 new TestInjector(mContext, new ArraySet<>(), mMappingFile,
-                        mMockUserManager, mMockDevicePolicyManager));
+                        mMockUserManager, mMockDevicePolicyManager,
+                        "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION"));
+
+        mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName());
     }
 
     private static class Listener implements IDumpstateListener {
@@ -359,10 +330,22 @@
         private boolean mBugreportStarted = false;
 
         TestInjector(Context context, ArraySet<String> allowlistedPackages, AtomicFile mappingFile,
-                UserManager um, DevicePolicyManager dpm) {
+                UserManager um, DevicePolicyManager dpm, String grantedRole) {
             super(context, allowlistedPackages, mappingFile);
             mUserManager = um;
             mDevicePolicyManager = dpm;
+
+            if (grantedRole != null) {
+                mRoleManagerWrapper =
+                        new BugreportManagerServiceImpl.Injector.RoleManagerWrapper() {
+                            @Override
+                            List<String> getRoleHolders(@NonNull String roleName) {
+                                return roleName.equals(grantedRole)
+                                        ? Collections.singletonList(mContext.getPackageName())
+                                        : Collections.emptyList();
+                            }
+                        };
+            }
         }
 
         @Override
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 9559a25..5fdb396 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -6095,6 +6095,67 @@
         assertThat(readPolicy.allowConversations()).isFalse();
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    @DisableFlags(Flags.FLAG_MODES_UI)
+    public void setNotificationPolicy_updatesRulePolicies_ifRulePolicyIsDefaultOrGlobalPolicy() {
+        ZenPolicy defaultZenPolicy = mZenModeHelper.getDefaultZenPolicy();
+        Policy previousManualPolicy = mZenModeHelper.mConfig.toNotificationPolicy();
+        ZenPolicy previousManualZenPolicy = ZenAdapters.notificationPolicyToZenPolicy(
+                previousManualPolicy);
+        ZenPolicy customZenPolicy = new ZenPolicy.Builder(defaultZenPolicy).allowConversations(
+                CONVERSATION_SENDERS_ANYONE).build();
+
+        mZenModeHelper.mConfig.automaticRules.clear();
+        addZenRule(mZenModeHelper.mConfig, "appWithDefault", "app.pkg",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, defaultZenPolicy);
+        addZenRule(mZenModeHelper.mConfig, "appWithSameAsManual", "app.pkg",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, previousManualZenPolicy);
+        addZenRule(mZenModeHelper.mConfig, "appWithCustom", "app.pkg",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, customZenPolicy);
+        addZenRule(mZenModeHelper.mConfig, "appWithOtherFilter", "app.pkg",
+                ZEN_MODE_ALARMS, null);
+        addZenRule(mZenModeHelper.mConfig, "systemWithDefault", "android",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, defaultZenPolicy);
+        addZenRule(mZenModeHelper.mConfig, "systemWithSameAsManual", "android",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, previousManualZenPolicy);
+
+        Policy newManualPolicy = new Policy(PRIORITY_CATEGORY_EVENTS, 0, 0);
+        mZenModeHelper.setNotificationPolicy(newManualPolicy, UPDATE_ORIGIN_USER, 0);
+        ZenPolicy newManualZenPolicy = ZenAdapters.notificationPolicyToZenPolicy(newManualPolicy);
+
+        // Only app rules with default or same-as-manual policies were updated.
+        assertThat(mZenModeHelper.mConfig.automaticRules.get("appWithDefault").zenPolicy)
+                .isEqualTo(newManualZenPolicy);
+        assertThat(mZenModeHelper.mConfig.automaticRules.get("appWithSameAsManual").zenPolicy)
+                .isEqualTo(newManualZenPolicy);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.get("appWithCustom").zenPolicy)
+                .isEqualTo(customZenPolicy);
+        assertThat(mZenModeHelper.mConfig.automaticRules.get("appWithOtherFilter").zenPolicy)
+                .isNull();
+        assertThat(mZenModeHelper.mConfig.automaticRules.get("systemWithDefault").zenPolicy)
+                .isEqualTo(defaultZenPolicy);
+        assertThat(mZenModeHelper.mConfig.automaticRules.get("systemWithSameAsManual").zenPolicy)
+                .isEqualTo(previousManualZenPolicy);
+    }
+
+    private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
+            @Nullable ZenPolicy zenPolicy) {
+        ZenRule rule = new ZenRule();
+        rule.id = id;
+        rule.pkg = ownerPkg;
+        rule.enabled = true;
+        rule.zenMode = zenMode;
+        rule.zenPolicy = zenPolicy;
+        // Plus stuff so that isValidAutomaticRule() passes
+        rule.name = String.format("Rule %s from %s with mode=%s and policy=%s", id, ownerPkg,
+                zenMode, zenPolicy);
+        rule.conditionId = Uri.parse(rule.name);
+
+        config.automaticRules.put(id, rule);
+    }
+
     private static final Correspondence<ZenRule, ZenRule> IGNORE_METADATA =
             Correspondence.transforming(zr -> {
                 Parcel p = Parcel.obtain();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index feef6b1..f92387c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -26,7 +26,6 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
-import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
 
 import static org.junit.Assert.assertEquals;
@@ -97,12 +96,12 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void testReturnsDoneIfDesktopWindowingIsEnabledAndUnsupportedDeviceOverridden() {
+    public void testReturnsContinueIfDesktopWindowingIsEnabledAndUnsupportedDeviceOverridden() {
         setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
                 /*enforceDeviceRestrictions=*/ false);
 
         final Task task = new TaskBuilder(mSupervisor).build();
-        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
     }
 
     @Test
@@ -135,22 +134,22 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void testReturnsDoneIfTaskUsingActivityTypeStandard() {
+    public void testReturnsContinueIfTaskUsingActivityTypeStandard() {
         setupDesktopModeLaunchParamsModifier();
 
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
-        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void testReturnsDoneIfTaskUsingActivityTypeUndefined() {
+    public void testReturnsContinueIfTaskUsingActivityTypeUndefined() {
         setupDesktopModeLaunchParamsModifier();
 
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_UNDEFINED).build();
-        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
     }
 
     @Test
@@ -176,7 +175,7 @@
         task.getDisplayArea().setBounds(new Rect(0, 0, displayWidth, displayHeight));
         final int desiredWidth = (int) (displayWidth * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight = (int) (displayHeight * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
-        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -192,7 +191,7 @@
         mCurrent.mPreferredTaskDisplayArea = mockTaskDisplayArea;
         mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
 
-        assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
         assertEquals(mockTaskDisplayArea, mResult.mPreferredTaskDisplayArea);
         assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
     }
diff --git a/tests/Input/src/com/android/server/input/InputShellCommandTest.java b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
index f4845a5..11f4633 100644
--- a/tests/Input/src/com/android/server/input/InputShellCommandTest.java
+++ b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
@@ -125,6 +125,14 @@
         assertThat(mInputEventInjector.mInjectedEvents).isEmpty();
     }
 
+    @Test
+    public void testInvalidKeyEventCommandArgsCombination() {
+        // --duration and --longpress must not be sent together
+        runCommand("keyevent --duration 1000 --longpress KEYCODE_A");
+
+        assertThat(mInputEventInjector.mInjectedEvents).isEmpty();
+    }
+
     private InputEvent getSingleInjectedInputEvent() {
         assertThat(mInputEventInjector.mInjectedEvents).hasSize(1);
         return mInputEventInjector.mInjectedEvents.get(0);
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
index eca258c..f6885e1 100644
--- a/tools/hoststubgen/TEST_MAPPING
+++ b/tools/hoststubgen/TEST_MAPPING
@@ -1,13 +1,63 @@
+// Keep the following two TEST_MAPPINGs in sync:
+// frameworks/base/ravenwood/TEST_MAPPING
+// frameworks/base/tools/hoststubgen/TEST_MAPPING
 {
   "presubmit": [
     { "name": "tiny-framework-dump-test" },
     { "name": "hoststubgentest" },
-    { "name": "hoststubgen-invoke-test" }
+    { "name": "hoststubgen-invoke-test" },
+    {
+      "name": "RavenwoodMockitoTest_device"
+    },
+    {
+      "name": "RavenwoodBivalentTest_device"
+    },
+    // The sysui tests should match vendor/unbundled_google/packages/SystemUIGoogle/TEST_MAPPING
+    {
+      "name": "SystemUIGoogleTests",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ],
+  "presubmit-large": [
+    {
+      "name": "SystemUITests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
   ],
   "ravenwood-presubmit": [
     {
+      "name": "RavenwoodMinimumTest",
+      "host": true
+    },
+    {
+      "name": "RavenwoodMockitoTest",
+      "host": true
+    },
+    {
       "name": "CtsUtilTestCasesRavenwood",
       "host": true
+    },
+    {
+      "name": "RavenwoodCoreTest",
+      "host": true
+    },
+    {
+      "name": "RavenwoodBivalentTest",
+      "host": true
     }
   ]
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 2f432cc..7212beb 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -16,6 +16,7 @@
 package com.android.hoststubgen
 
 import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.dumper.ApiDumper
 import com.android.hoststubgen.filters.AnnotationBasedFilter
 import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
 import com.android.hoststubgen.filters.ConstantFilter
@@ -89,7 +90,11 @@
             log.i("Dump file created at $it")
         }
         options.apiListFile.ifSet {
-            PrintWriter(it).use { pw -> stats.dumpApis(pw) }
+            PrintWriter(it).use { pw ->
+                // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
+                // framework-minus-apex.jar so that we can dump inherited methods from it.
+                ApiDumper(pw, allClasses, null, filter).dump()
+            }
             log.i("API list file created at $it")
         }
     }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
index da61469..9045db2 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
@@ -15,7 +15,8 @@
  */
 package com.android.hoststubgen
 
-import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.asm.getOuterClassNameFromFullClassName
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
 import com.android.hoststubgen.filters.FilterPolicyWithReason
 import org.objectweb.asm.Opcodes
 import java.io.PrintWriter
@@ -55,8 +56,8 @@
         // Ignore methods where policy isn't relevant
         if (policy.isIgnoredForStats) return
 
-        val packageName = resolvePackageName(fullClassName)
-        val className = resolveOuterClassName(fullClassName)
+        val packageName = getPackageNameFromFullClassName(fullClassName)
+        val className = getOuterClassNameFromFullClassName(fullClassName)
 
         // Ignore methods for certain generated code
         if (className.endsWith("Proto")
@@ -88,42 +89,4 @@
             }
         }
     }
-
-    fun dumpApis(pw: PrintWriter) {
-        pw.printf("PackageName,ClassName,MethodName,MethodDesc\n")
-        apis.sortedWith(compareBy({ it.fullClassName }, { it.methodName }, { it.methodDesc }))
-            .forEach { api ->
-            pw.printf(
-                "%s,%s,%s,%s\n",
-                csvEscape(resolvePackageName(api.fullClassName)),
-                csvEscape(resolveClassName(api.fullClassName)),
-                csvEscape(api.methodName),
-                csvEscape(api.methodDesc),
-                )
-        }
-    }
-
-    private fun resolvePackageName(fullClassName: String): String {
-        val start = fullClassName.lastIndexOf('/')
-        return fullClassName.substring(0, start).toHumanReadableClassName()
-    }
-
-    private fun resolveOuterClassName(fullClassName: String): String {
-        val start = fullClassName.lastIndexOf('/')
-        val end = fullClassName.indexOf('$')
-        if (end == -1) {
-            return fullClassName.substring(start + 1)
-        } else {
-            return fullClassName.substring(start + 1, end)
-        }
-    }
-
-    private fun resolveClassName(fullClassName: String): String {
-        val pos = fullClassName.lastIndexOf('/')
-        if (pos == -1) {
-            return fullClassName
-        } else {
-            return fullClassName.substring(pos + 1)
-        }
-    }
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 83e122f..b8d1800 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -76,17 +76,45 @@
     return null
 }
 
-private val removeLastElement = """[./][^./]*$""".toRegex()
+val periodOrSlash = charArrayOf('.', '/')
 
-fun getPackageNameFromClassName(className: String): String {
-    return className.replace(removeLastElement, "")
+fun getPackageNameFromFullClassName(fullClassName: String): String {
+    val pos = fullClassName.lastIndexOfAny(periodOrSlash)
+    if (pos == -1) {
+        return ""
+    } else {
+        return fullClassName.substring(0, pos)
+    }
 }
 
-fun resolveClassName(className: String, packageName: String): String {
+fun getClassNameFromFullClassName(fullClassName: String): String {
+    val pos = fullClassName.lastIndexOfAny(periodOrSlash)
+    if (pos == -1) {
+        return fullClassName
+    } else {
+        return fullClassName.substring(pos + 1)
+    }
+}
+
+fun getOuterClassNameFromFullClassName(fullClassName: String): String {
+    val start = fullClassName.lastIndexOfAny(periodOrSlash)
+    val end = fullClassName.indexOf('$')
+    if (end == -1) {
+        return fullClassName.substring(start + 1)
+    } else {
+        return fullClassName.substring(start + 1, end)
+    }
+}
+
+/**
+ * If [className] is a fully qualified name, just return it.
+ * Otherwise, prepend [defaultPackageName].
+ */
+fun resolveClassNameWithDefaultPackage(className: String, defaultPackageName: String): String {
     if (className.contains('.') || className.contains('/')) {
         return className
     }
-    return "$packageName.$className"
+    return "$defaultPackageName.$className"
 }
 
 fun String.toJvmClassName(): String {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
new file mode 100644
index 0000000..aaefee4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
@@ -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.hoststubgen.dumper
+
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.CTOR_NAME
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.getClassNameFromFullClassName
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.csvEscape
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.log
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.ClassNode
+import java.io.PrintWriter
+
+/**
+ * Dump all the API methods in [classes], with inherited methods, with their policies.
+ */
+class ApiDumper(
+    val pw: PrintWriter,
+    val classes: ClassNodes,
+    val frameworkClasses: ClassNodes?,
+    val filter: OutputFilter,
+) {
+    private data class MethodKey(
+        val name: String,
+        val descriptor: String,
+    )
+
+    val javaStandardApiPolicy = FilterPolicy.Stub.withReason("Java standard API")
+
+    private val shownMethods = mutableSetOf<MethodKey>()
+
+    /**
+     * Do the dump.
+     */
+    fun dump() {
+        pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" +
+                ",Supported,Policy,Reason\n")
+
+        classes.forEach { classNode ->
+            shownMethods.clear()
+            dump(classNode, classNode)
+        }
+    }
+
+    private fun dumpMethod(
+        classPackage: String,
+        className: String,
+        isSuperClass: Boolean,
+        methodClassName: String,
+        methodName: String,
+        methodDesc: String,
+        policy: FilterPolicyWithReason,
+    ) {
+        pw.printf(
+            "%s,%s,%d,%s,%s,%s,%d,%s,%s\n",
+            csvEscape(classPackage),
+            csvEscape(className),
+            if (isSuperClass) { 1 } else { 0 },
+            csvEscape(methodClassName),
+            csvEscape(methodName),
+            csvEscape(methodDesc),
+            if (policy.policy.isSupported) { 1 } else { 0 },
+            policy.policy,
+            csvEscape(policy.reason),
+        )
+    }
+
+    private fun isDuplicate(methodName: String, methodDesc: String): Boolean {
+        val methodKey = MethodKey(methodName, methodDesc)
+
+        if (shownMethods.contains(methodKey)) {
+            return true
+        }
+        shownMethods.add(methodKey)
+        return false
+    }
+
+    private fun dump(
+        dumpClass: ClassNode,
+        methodClass: ClassNode,
+        ) {
+        val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+        val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+
+        val isSuperClass = dumpClass != methodClass
+
+        methodClass.methods?.sortedWith(compareBy({ it.name }, { it.desc }))?.forEach { method ->
+
+            // Don't print ctor's from super classes.
+            if (isSuperClass) {
+                if (CTOR_NAME == method.name || CLASS_INITIALIZER_NAME == method.name) {
+                    return@forEach
+                }
+            }
+            // If we already printed the method from a subclass, don't print it.
+            if (isDuplicate(method.name, method.desc)) {
+                return@forEach
+            }
+
+            val policy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc)
+
+            // Let's skip "Remove" APIs. Ideally we want to print it, just to make the CSV
+            // complete, we still need to hide methods substituted (== @RavenwoodReplace) methods
+            // and for now we don't have an easy way to detect it.
+            if (policy.policy == FilterPolicy.Remove) {
+                return@forEach
+            }
+
+            val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc)
+
+            dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(),
+                renameTo ?: method.name, method.desc, policy)
+       }
+
+        // Dump super class methods.
+        dumpSuper(dumpClass, methodClass.superName)
+
+        // Dump interface methods (which may have default methods).
+        methodClass.interfaces?.sorted()?.forEach { interfaceName ->
+            dumpSuper(dumpClass, interfaceName)
+        }
+    }
+
+    /**
+     * Dump a given super class / interface.
+     */
+    private fun dumpSuper(
+        dumpClass: ClassNode,
+        methodClassName: String,
+    ) {
+        classes.findClass(methodClassName)?.let { methodClass ->
+            dump(dumpClass, methodClass)
+            return
+        }
+        frameworkClasses?.findClass(methodClassName)?.let { methodClass ->
+            dump(dumpClass, methodClass)
+            return
+        }
+        if (methodClassName.startsWith("java/") ||
+            methodClassName.startsWith("javax/")
+            ) {
+            dumpStandardClass(dumpClass, methodClassName)
+            return
+        }
+        log.w("Super class or interface $methodClassName (used by ${dumpClass.name}) not found.")
+    }
+
+    /**
+     * Dump methods from Java standard classes.
+     */
+    private fun dumpStandardClass(
+        dumpClass: ClassNode,
+        methodClassName: String,
+    ) {
+        val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+        val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
+
+        val methodClassName = methodClassName.toHumanReadableClassName()
+
+        try {
+            val clazz = Class.forName(methodClassName)
+
+            // Method.getMethods() returns only public methods, but with inherited ones.
+            // Method.getDeclaredMethods() returns private methods too, but no inherited methods.
+            //
+            // Since we're only interested in public ones, just use getMethods().
+            clazz.methods.forEach { method ->
+                val methodName = method.name
+                val methodDesc = Type.getMethodDescriptor(method)
+
+                // If we already printed the method from a subclass, don't print it.
+                if (isDuplicate(methodName, methodDesc)) {
+                    return@forEach
+                }
+
+                dumpMethod(pkg, cls, true, methodClassName,
+                    methodName, methodDesc, javaStandardApiPolicy)
+            }
+        } catch (e: ClassNotFoundException) {
+            log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.")
+        }
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index 45e140c..6643492 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -20,8 +20,8 @@
 import com.android.hoststubgen.LogLevel
 import com.android.hoststubgen.asm.ClassNodes
 import com.android.hoststubgen.asm.UnifiedVisitor
-import com.android.hoststubgen.asm.getPackageNameFromClassName
-import com.android.hoststubgen.asm.resolveClassName
+import com.android.hoststubgen.asm.getPackageNameFromFullClassName
+import com.android.hoststubgen.asm.resolveClassNameWithDefaultPackage
 import com.android.hoststubgen.asm.toJvmClassName
 import com.android.hoststubgen.filters.FilterPolicy
 import com.android.hoststubgen.filters.FilterPolicyWithReason
@@ -89,7 +89,7 @@
     ) {
         super.visit(version, access, name, signature, superName, interfaces)
         currentClassName = name
-        currentPackageName = getPackageNameFromClassName(name)
+        currentPackageName = getPackageNameFromFullClassName(name)
         classPolicy = filter.getPolicyForClass(currentClassName)
 
         log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName)
@@ -98,7 +98,8 @@
         log.indent()
 
         filter.getNativeSubstitutionClass(currentClassName)?.let { className ->
-            val fullClassName = resolveClassName(className, currentPackageName).toJvmClassName()
+            val fullClassName = resolveClassNameWithDefaultPackage(className, currentPackageName)
+                .toJvmClassName()
             log.d("  NativeSubstitutionClass: $fullClassName")
             if (classes.findClass(fullClassName) == null) {
                 log.w("Native substitution class $fullClassName not found. Class must be " +