Merge "Improve stability of battery stats history unparceling" 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/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index 2ace602..d80d3c8 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -18,6 +18,8 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
+
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
@@ -35,6 +37,7 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.view.WindowRelayoutResult;
 import android.widget.LinearLayout;
 import android.window.ClientWindowFrames;
 
@@ -128,7 +131,8 @@
         final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
         final InsetsState mOutInsetsState = new InsetsState();
         final InsetsSourceControl.Array mOutControls = new InsetsSourceControl.Array();
-        final Bundle mOutBundle = new Bundle();
+        final Bundle mOutBundle = windowSessionRelayoutInfo() ? null : new Bundle();
+        final WindowRelayoutResult mOutRelayoutResult;
         final IWindow mWindow;
         final View mView;
         final WindowManager.LayoutParams mParams;
@@ -148,16 +152,26 @@
             mHeight = mView.getMeasuredHeight();
             mOutSurfaceControl = mView.getViewRootImpl().getSurfaceControl();
             mViewVisibility = visibilitySupplier;
+            mOutRelayoutResult = windowSessionRelayoutInfo()
+                    ? new WindowRelayoutResult(mOutFrames, mOutMergedConfiguration,
+                            mOutSurfaceControl, mOutInsetsState, mOutControls)
+                    : null;
         }
 
         void runBenchmark(BenchmarkState state) throws RemoteException {
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             while (state.keepRunning()) {
                 mRelayoutSeq++;
-                session.relayout(mWindow, mParams, mWidth, mHeight,
-                        mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */,
-                        mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState,
-                        mOutControls, mOutBundle);
+                if (windowSessionRelayoutInfo()) {
+                    session.relayout(mWindow, mParams, mWidth, mHeight,
+                            mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */,
+                            mOutRelayoutResult);
+                } else {
+                    session.relayoutLegacy(mWindow, mParams, mWidth, mHeight,
+                            mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */,
+                            mOutFrames, mOutMergedConfiguration, mOutSurfaceControl,
+                            mOutInsetsState, mOutControls, mOutBundle);
+                }
             }
         }
     }
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/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 7a39610..eb12ada 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -107,22 +107,25 @@
     private static final long MIN_ADJUST_CALLBACK_INTERVAL_MS = 1_000L;
 
     private static final int UNBYPASSABLE_BG_BLOCKED_REASONS =
-            ~ConnectivityManager.BLOCKED_REASON_NONE;
+            ~ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND;
     private static final int UNBYPASSABLE_EJ_BLOCKED_REASONS =
             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
+                    | ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND
                     | ConnectivityManager.BLOCKED_REASON_DOZE);
     private static final int UNBYPASSABLE_UI_BLOCKED_REASONS =
             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
                     | ConnectivityManager.BLOCKED_REASON_DOZE
                     | ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER
+                    | ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND
                     | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED);
     private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS =
             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
                     | ConnectivityManager.BLOCKED_REASON_DOZE
                     | ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER
+                    | ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND
                     | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED);
 
     @VisibleForTesting
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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bc66127..68cc17b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -353,6 +353,9 @@
 
     private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent";
 
+    private static final long BINDER_CALLBACK_THROTTLE = 10_100L;
+    private long mBinderCallbackLast = -1;
+
     /**
      * Denotes the sequence number of the process state change for which the main thread needs
      * to block until the network rules are updated for it.
@@ -7525,6 +7528,12 @@
         Binder.setTransactionCallback(new IBinderCallback() {
             @Override
             public void onTransactionError(int pid, int code, int flags, int err) {
+                final long now = SystemClock.uptimeMillis();
+                if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE) {
+                    Slog.d(TAG, "Too many transaction errors, throttling freezer binder callback.");
+                    return;
+                }
+                mBinderCallbackLast = now;
                 try {
                     mgr.frozenBinderTransactionDetected(pid, code, flags, err);
                 } catch (RemoteException ex) {
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/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index b5e5074..62820ad 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -1032,9 +1032,7 @@
             return false;
         }
         boolean success;
-        try {
-            LockscreenCredential credential = createLockscreenCredential(
-                    lockType, password);
+        try (LockscreenCredential credential = createLockscreenCredential(lockType, password)) {
             success = mLockPatternUtils.setLockCredential(
                     credential,
                     /* savedPassword= */ LockscreenCredential.createNone(),
@@ -1213,19 +1211,20 @@
     public boolean setLock(@LockTypes int newLockType, @Nullable byte[] newPassword,
             @LockTypes int currentLockType, @Nullable byte[] currentPassword) {
         final int userId = mContext.getUserId();
-        LockscreenCredential currentCredential = createLockscreenCredential(
+        try (LockscreenCredential currentCredential = createLockscreenCredential(
                 currentLockType, currentPassword);
-        LockscreenCredential newCredential = createLockscreenCredential(
-                newLockType, newPassword);
-        PasswordMetrics adminMetrics =
-                mLockPatternUtils.getRequestedPasswordMetrics(mContext.getUserId());
-        List<PasswordValidationError> errors = PasswordMetrics.validateCredential(adminMetrics,
-                DevicePolicyManager.PASSWORD_COMPLEXITY_NONE, newCredential);
-        if (!errors.isEmpty()) {
-            Log.e(TAG, "New credential is not valid: " + errors.get(0));
-            return false;
+                LockscreenCredential newCredential = createLockscreenCredential(
+                        newLockType, newPassword)) {
+            PasswordMetrics adminMetrics =
+                    mLockPatternUtils.getRequestedPasswordMetrics(mContext.getUserId());
+            List<PasswordValidationError> errors = PasswordMetrics.validateCredential(adminMetrics,
+                    DevicePolicyManager.PASSWORD_COMPLEXITY_NONE, newCredential);
+            if (!errors.isEmpty()) {
+                Log.e(TAG, "New credential is not valid: " + errors.get(0));
+                return false;
+            }
+            return mLockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
         }
-        return mLockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
     }
 
     /**
@@ -1244,14 +1243,14 @@
             Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
     })
     public boolean checkLock(@LockTypes int lockType, @Nullable byte[] password) {
-        final LockscreenCredential credential = createLockscreenCredential(
-                lockType, password);
-        final VerifyCredentialResponse response = mLockPatternUtils.verifyCredential(
-                credential, mContext.getUserId(), /* flags= */ 0);
-        if (response == null) {
-            return false;
+        try (LockscreenCredential credential = createLockscreenCredential(lockType, password)) {
+            final VerifyCredentialResponse response = mLockPatternUtils.verifyCredential(
+                    credential, mContext.getUserId(), /* flags= */ 0);
+            if (response == null) {
+                return false;
+            }
+            return response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
         }
-        return response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
     }
 
     /** Starts a session to verify lockscreen credentials provided by a remote device.
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/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 31c9a258..18914e1 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -275,13 +275,23 @@
 }
 
 flag {
-    name: "headless_single_user_bad_device_admin_state_fix"
-    namespace: "enterprise"
-    description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
-    bug: "332477138"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
+  name: "headless_single_user_bad_device_admin_state_fix"
+  namespace: "enterprise"
+  description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
+  bug: "332477138"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "onboarding_bugreport_storage_bug_fix"
+  namespace: "enterprise"
+  description: "Add a separate storage limit for deferred bugreports"
+  bug: "330177040"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
 
 flag {
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 6158917..205f1e9 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -240,3 +240,11 @@
     bug: "297603927"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "component_state_changed_metrics"
+    namespace: "package_manager_service"
+    description: "Feature flag to log the metrics when the component state is changed."
+    bug: "316916801"
+    is_fixed_read_only: true
+}
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/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 23d6007..00ba3bf 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -133,6 +133,7 @@
     int getUserStatusBarIconResId(int userId);
     boolean hasBadge(int userId);
     int getProfileLabelResId(int userId);
+    int getProfileAccessibilityLabelResId(int userId);
     boolean isUserUnlocked(int userId);
     boolean isUserRunning(int userId);
     boolean isUserForeground(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 857a85d..d38354d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -18,6 +18,7 @@
 
 import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
 import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.STATUS_BAR_WORK_ICON_ACCESSIBILITY;
 import static android.app.admin.DevicePolicyResources.UNDEFINED;
 
 import android.Manifest;
@@ -5954,6 +5955,43 @@
     }
 
     /**
+     * Returns the string used to represent the profile associated with the given userId. This
+     * string typically includes the profile name used by accessibility services like TalkBack.
+     * @hide
+     *
+     * @return String representing the accessibility label for the given profile user.
+     *
+     * @throws android.content.res.Resources.NotFoundException if the user does not have a label
+     * defined.
+     */
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.QUERY_USERS,
+                    Manifest.permission.INTERACT_ACROSS_USERS})
+    public String getProfileAccessibilityString(int userId) {
+        if (isManagedProfile(mUserId)) {
+            DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+            dpm.getResources().getString(
+                    STATUS_BAR_WORK_ICON_ACCESSIBILITY,
+                    () -> getProfileAccessibilityLabel(userId));
+        }
+        return getProfileAccessibilityLabel(userId);
+    }
+
+    private String getProfileAccessibilityLabel(int userId) {
+        try {
+            final int resourceId = mService.getProfileAccessibilityLabelResId(userId);
+            return Resources.getSystem().getString(resourceId);
+        } catch (Resources.NotFoundException nfe) {
+            Log.e(TAG, "Accessibility label not defined for user " + userId);
+            throw nfe;
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * If the user is a {@link UserManager#isProfile profile}, checks if the user
      * shares media with its parent user (the user that created this profile).
      * Returns false for any other type of user.
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/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index f87cb85..a42eaff 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -29,6 +29,16 @@
 }
 
 flag {
+    name: "dismiss_dream_on_keyguard_dismiss"
+    namespace: "systemui"
+    description: "Dismisses the dream in the keyguard-going-away transition, preventing it from being visible"
+    bug: "333829441"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
   name: "dream_tracks_focus"
   namespace: "communal"
   description: "This flag enables the ability for dreams to track whether or not they have focus"
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 582c90f..d174bef 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -30,6 +30,7 @@
 import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
 import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
 import static com.android.window.flags.Flags.offloadColorExtraction;
+import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
 
 import android.animation.AnimationHandler;
 import android.animation.Animator;
@@ -105,6 +106,7 @@
 import android.view.WindowLayout;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.view.WindowRelayoutResult;
 import android.window.ActivityWindowInfo;
 import android.window.ClientWindowFrames;
 import android.window.ScreenCapture;
@@ -299,7 +301,14 @@
         final InsetsState mInsetsState = new InsetsState();
         final InsetsSourceControl.Array mTempControls = new InsetsSourceControl.Array();
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
-        final Bundle mSyncSeqIdBundle = new Bundle();
+        final Bundle mSyncSeqIdBundle = windowSessionRelayoutInfo() ? null : new Bundle();
+
+        SurfaceControl mSurfaceControl = new SurfaceControl();
+        WindowRelayoutResult mRelayoutResult = windowSessionRelayoutInfo()
+                ? new WindowRelayoutResult(mWinFrames, mMergedConfiguration, mSurfaceControl,
+                        mInsetsState, mTempControls)
+                : null;
+
         private final Point mSurfaceSize = new Point();
         private final Point mLastSurfaceSize = new Point();
         private final Matrix mTmpMatrix = new Matrix();
@@ -357,8 +366,6 @@
         private float mWallpaperDimAmount = 0f;
         private float mPreviousWallpaperDimAmount = mWallpaperDimAmount;
         private float mDefaultDimAmount = 0.05f;
-
-        SurfaceControl mSurfaceControl = new SurfaceControl();
         SurfaceControl mBbqSurfaceControl;
         BLASTBufferQueue mBlastBufferQueue;
         private SurfaceControl mScreenshotSurfaceControl;
@@ -1268,9 +1275,15 @@
                     } else {
                         mLayout.surfaceInsets.set(0, 0, 0, 0);
                     }
-                    final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
-                            View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
-                            mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+                    final int relayoutResult;
+                    if (windowSessionRelayoutInfo()) {
+                        relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
+                                View.VISIBLE, 0, 0, 0, mRelayoutResult);
+                    } else {
+                        relayoutResult = mSession.relayoutLegacy(mWindow, mLayout, mWidth, mHeight,
+                                View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
+                                mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+                    }
                     final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration()
                             .windowConfiguration.getMaxBounds();
                     if (!outMaxBounds.equals(maxBounds)) {
@@ -2364,6 +2377,7 @@
             if (mSurfaceControl != null) {
                 mSurfaceControl.release();
                 mSurfaceControl = null;
+                mRelayoutResult = null;
             }
         }
 
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 3a90841..86264eb 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -35,6 +35,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
+import android.view.WindowRelayoutResult;
 import android.window.ClientWindowFrames;
 import android.window.InputTransferToken;
 import android.window.OnBackInvokedCallbackInfo;
@@ -84,6 +85,16 @@
     void remove(IBinder clientToken);
 
     /**
+     * @deprecated
+     */
+    int relayoutLegacy(IWindow window, in WindowManager.LayoutParams attrs,
+            int requestedWidth, int requestedHeight, int viewVisibility,
+            int flags, int seq, int lastSyncSeqId, out ClientWindowFrames outFrames,
+            out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
+            out InsetsState insetsState, out InsetsSourceControl.Array activeControls,
+            out Bundle bundle);
+
+    /**
      * Change the parameters of a window.  You supply the
      * new parameters, it returns the new frame of the window on screen (the
      * position should be ignored) and surface of the window.  The surface
@@ -98,22 +109,12 @@
      * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}.
      * @param seq The calling sequence of {@link #relayout} and {@link #relayoutAsync}.
      * @param lastSyncSeqId The last SyncSeqId that the client applied.
-     * @param outFrames The window frames used by the client side for layout.
-     * @param outMergedConfiguration New config container that holds global, override and merged
-     *                               config for window, if it is now becoming visible and the merged
-     *                               config has changed since it was last displayed.
-     * @param outSurfaceControl Object in which is placed the new display surface.
-     * @param insetsState The current insets state in the system.
-     * @param activeControls Objects which allow controlling {@link InsetsSource}s.
-     * @param bundle A Bundle to contain the latest SyncSeqId and any extra relayout optional infos.
+     * @param outRelayoutResult Data object contains the info to be returned from server side.
      * @return int Result flags, defined in {@link WindowManagerGlobal}.
      */
-    int relayout(IWindow window, in WindowManager.LayoutParams attrs,
-            int requestedWidth, int requestedHeight, int viewVisibility,
-            int flags, int seq, int lastSyncSeqId, out ClientWindowFrames outFrames,
-            out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
-            out InsetsState insetsState, out InsetsSourceControl.Array activeControls,
-            out Bundle bundle);
+    int relayout(IWindow window, in WindowManager.LayoutParams attrs, int requestedWidth,
+            int requestedHeight, int viewVisibility, int flags, int seq, int lastSyncSeqId,
+            out @nullable WindowRelayoutResult outRelayoutResult);
 
     /**
      * Similar to {@link #relayout} but this is an oneway method which doesn't return anything.
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index e14ddd6..1afedc1 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -63,8 +63,8 @@
     private boolean mIsPreCommitAnimationInProgress = false;
     private int mStartRootScrollY = 0;
 
-    public ImeBackAnimationController(ViewRootImpl viewRoot) {
-        mInsetsController = viewRoot.getInsetsController();
+    public ImeBackAnimationController(ViewRootImpl viewRoot, InsetsController insetsController) {
+        mInsetsController = insetsController;
         mViewRoot = viewRoot;
     }
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 85c779b..6568912 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -95,7 +95,7 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final InsetsState mInitialInsetsState;
     private final @AnimationType int mAnimationType;
-    private final @LayoutInsetsDuringAnimation int mLayoutInsetsDuringAnimation;
+    private @LayoutInsetsDuringAnimation int mLayoutInsetsDuringAnimation;
     private final @InsetsType int mTypes;
     private @InsetsType int mControllingTypes;
     private final InsetsAnimationControlCallbacks mController;
@@ -377,6 +377,12 @@
     }
 
     @Override
+    public void updateLayoutInsetsDuringAnimation(
+            @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+        mLayoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
+    }
+
+    @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(IS_CANCELLED, mCancelled);
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index cf40e7e..8cb8b47 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -20,6 +20,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsController.AnimationType;
+import android.view.InsetsController.LayoutInsetsDuringAnimation;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.inputmethod.ImeTracker;
 
@@ -82,6 +83,14 @@
     ImeTracker.Token getStatsToken();
 
     /**
+     * Updates the desired layout insets during the animation.
+     *
+     * @param layoutInsetsDuringAnimation Whether the insets should be shown or hidden
+     */
+    void updateLayoutInsetsDuringAnimation(
+            @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation);
+
+    /**
      *
      * Export the state of classes that implement this interface into a protocol buffer
      * output stream.
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 92e20e0..83ff88b 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -186,4 +186,11 @@
     public int getAnimationType() {
         return mControl.getAnimationType();
     }
+
+    @Override
+    public void updateLayoutInsetsDuringAnimation(
+            @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+        InsetsAnimationThread.getHandler().post(
+                () -> mControl.updateLayoutInsetsDuringAnimation(layoutInsetsDuringAnimation));
+    }
 }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6c90011..c545267 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1028,8 +1028,23 @@
         reportRequestedVisibleTypes();
     }
 
-    void setPredictiveBackImeHideAnimInProgress(boolean isInProgress) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void setPredictiveBackImeHideAnimInProgress(boolean isInProgress) {
         mIsPredictiveBackImeHideAnimInProgress = isInProgress;
+        if (isInProgress) {
+            // The InsetsAnimationControlRunner has layoutInsetsDuringAnimation set to SHOWN during
+            // predictive back. Let's set it to HIDDEN once the predictive back animation enters the
+            // post-commit phase.
+            // That prevents flickers in case the animation is cancelled by an incoming show request
+            // during the hide animation.
+            for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+                final InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner;
+                if ((runner.getTypes() & ime()) != 0) {
+                    runner.updateLayoutInsetsDuringAnimation(LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+                    break;
+                }
+            }
+        }
     }
 
     boolean isPredictiveBackImeHideAnimInProgress() {
@@ -1231,7 +1246,8 @@
                 false /* fromPredictiveBack */);
     }
 
-    void controlWindowInsetsAnimation(@InsetsType int types,
+    @VisibleForTesting(visibility = PACKAGE)
+    public void controlWindowInsetsAnimation(@InsetsType int types,
             @Nullable CancellationSignal cancellationSignal,
             WindowInsetsAnimationControlListener listener,
             boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
@@ -1983,7 +1999,8 @@
         }
     }
 
-    Host getHost() {
+    @VisibleForTesting(visibility = PACKAGE)
+    public Host getHost() {
         return mHost;
     }
 }
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index ebdddd5..6e62221 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -34,6 +34,7 @@
 import android.graphics.Rect;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
+import android.view.InsetsController.LayoutInsetsDuringAnimation;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
 import android.view.animation.Interpolator;
@@ -242,4 +243,9 @@
     @Override
     public void onCancelled(WindowInsetsAnimationController controller) {
     }
+
+    @Override
+    public void updateLayoutInsetsDuringAnimation(
+            @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+    }
 }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 56edfe72..e20b15e 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -835,8 +835,8 @@
     @Override
     public String toString() {
         synchronized (mLock) {
-            return "Surface(name=" + mName + ")/@0x" +
-                    Integer.toHexString(System.identityHashCode(this));
+            return "Surface(name=" + mName + " mNativeObject=" + mNativeObject + ")/@0x"
+                    + Integer.toHexString(System.identityHashCode(this));
         }
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2cb862c..f0d27da 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -110,6 +110,7 @@
 import static android.view.accessibility.Flags.fixMergedContentChangeEventV2;
 import static android.view.accessibility.Flags.forceInvertColor;
 import static android.view.accessibility.Flags.reduceWindowContentChangedEventThrottle;
+import static android.view.flags.Flags.addSchandleToVriSurface;
 import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.view.flags.Flags.toolkitFrameRateFunctionEnablingReadOnly;
 import static android.view.flags.Flags.toolkitFrameRateTypingReadOnly;
@@ -125,6 +126,7 @@
 import static com.android.window.flags.Flags.activityWindowInfoFlag;
 import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
 import static com.android.window.flags.Flags.setScPropertiesInClient;
+import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -1133,7 +1135,14 @@
      * we get rid of synchronous relayout, until then, we use this bundle to channel the
      * integer back over relayout.
      */
-    private Bundle mRelayoutBundle = new Bundle();
+    private final Bundle mRelayoutBundle = windowSessionRelayoutInfo()
+            ? null
+            : new Bundle();
+
+    private final WindowRelayoutResult mRelayoutResult = windowSessionRelayoutInfo()
+            ? new WindowRelayoutResult(mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
+                    mTempInsets, mTempControls)
+            : null;
 
     private static volatile boolean sAnrReported = false;
     static BLASTBufferQueue.TransactionHangCallback sTransactionHangCallback =
@@ -1234,7 +1243,7 @@
         // TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
         mChoreographer = Choreographer.getInstance();
         mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
-        mImeBackAnimationController = new ImeBackAnimationController(this);
+        mImeBackAnimationController = new ImeBackAnimationController(this, mInsetsController);
         mHandwritingInitiator = new HandwritingInitiator(
                 mViewConfiguration,
                 mContext.getSystemService(InputMethodManager.class));
@@ -2624,7 +2633,12 @@
         mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
                 mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
         mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
-        Surface blastSurface = mBlastBufferQueue.createSurface();
+        Surface blastSurface;
+        if (addSchandleToVriSurface()) {
+            blastSurface = mBlastBufferQueue.createSurfaceWithHandle();
+        } else {
+            blastSurface = mBlastBufferQueue.createSurface();
+        }
         // Only call transferFrom if the surface has changed to prevent inc the generation ID and
         // causing EGL resources to be recreated.
         mSurface.transferFrom(blastSurface);
@@ -4262,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;
         }
     }
 
@@ -5942,13 +5960,19 @@
         return handled;
     }
 
-    void setScrollY(int scrollY) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void setScrollY(int scrollY) {
         if (mScroller != null) {
             mScroller.abortAnimation();
         }
         mScrollY = scrollY;
     }
 
+    @VisibleForTesting
+    public int getScrollY() {
+        return mScrollY;
+    }
+
     /**
      * @hide
      */
@@ -9044,29 +9068,42 @@
                     insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
                     mLastSyncSeqId);
         } else {
-            relayoutResult = mWindowSession.relayout(mWindow, params,
-                    requestedWidth, requestedHeight, viewVisibility,
-                    insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
-                    mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
-                    mTempInsets, mTempControls, mRelayoutBundle);
+            if (windowSessionRelayoutInfo()) {
+                relayoutResult = mWindowSession.relayout(mWindow, params,
+                        requestedWidth, requestedHeight, viewVisibility,
+                        insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+                        mRelayoutSeq, mLastSyncSeqId, mRelayoutResult);
+            } else {
+                relayoutResult = mWindowSession.relayoutLegacy(mWindow, params,
+                        requestedWidth, requestedHeight, viewVisibility,
+                        insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+                        mRelayoutSeq, mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration,
+                        mSurfaceControl, mTempInsets, mTempControls, mRelayoutBundle);
+            }
             mRelayoutRequested = true;
 
             if (activityWindowInfoFlag() && mPendingActivityWindowInfo != null) {
                 ActivityWindowInfo outInfo = null;
-                try {
-                    outInfo = mRelayoutBundle.getParcelable(
-                            IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO,
-                            ActivityWindowInfo.class);
-                    mRelayoutBundle.remove(IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO);
-                } catch (IllegalStateException e) {
-                    Log.e(TAG, "Failed to get ActivityWindowInfo from relayout Bundle", e);
+                if (windowSessionRelayoutInfo()) {
+                    outInfo = mRelayoutResult != null ? mRelayoutResult.activityWindowInfo : null;
+                } else {
+                    try {
+                        outInfo = mRelayoutBundle.getParcelable(
+                                IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO,
+                                ActivityWindowInfo.class);
+                        mRelayoutBundle.remove(
+                                IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO);
+                    } catch (IllegalStateException e) {
+                        Log.e(TAG, "Failed to get ActivityWindowInfo from relayout Bundle", e);
+                    }
                 }
                 if (outInfo != null) {
                     mPendingActivityWindowInfo.set(outInfo);
                 }
             }
-            final int maybeSyncSeqId = mRelayoutBundle.getInt(
-                    IWindowSession.KEY_RELAYOUT_BUNDLE_SEQID);
+            final int maybeSyncSeqId = windowSessionRelayoutInfo()
+                    ? mRelayoutResult.syncSeqId
+                    : mRelayoutBundle.getInt(IWindowSession.KEY_RELAYOUT_BUNDLE_SEQID);
             if (maybeSyncSeqId > 0) {
                 mSyncSeqId = maybeSyncSeqId;
             }
@@ -12532,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/android/view/WindowRelayoutResult.aidl b/core/java/android/view/WindowRelayoutResult.aidl
new file mode 100644
index 0000000..3b4e340
--- /dev/null
+++ b/core/java/android/view/WindowRelayoutResult.aidl
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * Stores information to pass to {@link IWindowSession#relayout} as AIDL out type.
+ * @hide
+ */
+parcelable WindowRelayoutResult;
diff --git a/core/java/android/view/WindowRelayoutResult.java b/core/java/android/view/WindowRelayoutResult.java
new file mode 100644
index 0000000..882d3a7
--- /dev/null
+++ b/core/java/android/view/WindowRelayoutResult.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MergedConfiguration;
+import android.window.ActivityWindowInfo;
+import android.window.ClientWindowFrames;
+
+/**
+ * Stores information to pass to {@link IWindowSession#relayout} as AIDL out type.
+ * @hide
+ */
+public final class WindowRelayoutResult implements Parcelable {
+
+    /** The window frames used by the client side for layout. */
+    @NonNull
+    public final ClientWindowFrames frames;
+
+    /**
+     * New config container that holds global, override and merged config for window, if it is now
+     * becoming visible and the merged config has changed since it was last displayed.
+     */
+    @NonNull
+    public final MergedConfiguration mergedConfiguration;
+
+    /** Object in which is placed the new display surface. */
+    @NonNull
+    public final SurfaceControl surfaceControl;
+
+    /** The current insets state in the system. */
+    @NonNull
+    public final InsetsState insetsState;
+
+    /** Objects which allow controlling {@link InsetsSource}s. */
+    @NonNull
+    public final InsetsSourceControl.Array activeControls;
+
+    /** The latest sync seq id for the relayout configuration. */
+    public int syncSeqId;
+
+    /**
+     * The latest {@link ActivityWindowInfo} if this relayout window is an Activity window.
+     * {@code null} if this is not an Activity window.
+     */
+    @Nullable
+    public ActivityWindowInfo activityWindowInfo;
+
+    public WindowRelayoutResult() {
+        this(new ClientWindowFrames(), new MergedConfiguration(), new SurfaceControl(),
+                new InsetsState(), new InsetsSourceControl.Array());
+    }
+
+    /**
+     * Stores information to pass for {@link IWindowSession#relayout}.
+     *
+     * @param frames              The window frames used by the client side for layout.
+     * @param mergedConfiguration New config container that holds global, override and merged
+     *                            config for window, if it is now becoming visible and the merged
+     *                            config has changed since it was last displayed.
+     * @param surfaceControl      Object in which is placed the new display surface.
+     * @param insetsState         The current insets state in the system.
+     * @param activeControls      Objects which allow controlling {@link InsetsSource}s.
+     */
+    public WindowRelayoutResult(@NonNull ClientWindowFrames frames,
+            @NonNull MergedConfiguration mergedConfiguration,
+            @NonNull SurfaceControl surfaceControl, @NonNull InsetsState insetsState,
+            @NonNull InsetsSourceControl.Array activeControls) {
+        this.frames = requireNonNull(frames);
+        this.mergedConfiguration = requireNonNull(mergedConfiguration);
+        this.surfaceControl = requireNonNull(surfaceControl);
+        this.insetsState = requireNonNull(insetsState);
+        this.activeControls = requireNonNull(activeControls);
+    }
+
+    private WindowRelayoutResult(@NonNull Parcel in) {
+        this();
+        readFromParcel(in);
+    }
+
+    /** Needed for IBinder out parameter. */
+    public void readFromParcel(@NonNull Parcel in) {
+        frames.readFromParcel(in);
+        mergedConfiguration.readFromParcel(in);
+        surfaceControl.readFromParcel(in);
+        insetsState.readFromParcel(in);
+        activeControls.readFromParcel(in);
+        syncSeqId = in.readInt();
+        activityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        frames.writeToParcel(dest, flags);
+        mergedConfiguration.writeToParcel(dest, flags);
+        surfaceControl.writeToParcel(dest, flags);
+        insetsState.writeToParcel(dest, flags);
+        activeControls.writeToParcel(dest, flags);
+        dest.writeInt(syncSeqId);
+        dest.writeTypedObject(activityWindowInfo, flags);
+    }
+
+    @NonNull
+    public static final Creator<WindowRelayoutResult> CREATOR =
+            new Creator<>() {
+                @Override
+                public WindowRelayoutResult createFromParcel(@NonNull Parcel in) {
+                    return new WindowRelayoutResult(in);
+                }
+
+                @Override
+                public WindowRelayoutResult[] newArray(int size) {
+                    return new WindowRelayoutResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7309080..e6367ff 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -349,12 +349,49 @@
     }
 
     @Override
-    public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
+    public int relayoutLegacy(IWindow window, WindowManager.LayoutParams inAttrs,
             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
             int lastSyncSeqId, ClientWindowFrames outFrames,
             MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
             InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
             Bundle outSyncSeqIdBundle) {
+        return relayoutInner(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags,
+                seq, lastSyncSeqId, outFrames, outMergedConfiguration, outSurfaceControl,
+                outInsetsState, outActiveControls);
+    }
+
+    @Override
+    public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
+            int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+            int lastSyncSeqId, WindowRelayoutResult outRelayoutResult) {
+        final ClientWindowFrames outFrames;
+        final MergedConfiguration outMergedConfiguration;
+        final SurfaceControl outSurfaceControl;
+        final InsetsState outInsetsState;
+        final InsetsSourceControl.Array outActiveControls;
+        if (outRelayoutResult != null) {
+            outFrames = outRelayoutResult.frames;
+            outMergedConfiguration = outRelayoutResult.mergedConfiguration;
+            outSurfaceControl = outRelayoutResult.surfaceControl;
+            outInsetsState = outRelayoutResult.insetsState;
+            outActiveControls = outRelayoutResult.activeControls;
+        } else {
+            outFrames = null;
+            outMergedConfiguration = null;
+            outSurfaceControl = null;
+            outInsetsState = null;
+            outActiveControls = null;
+        }
+        return relayoutInner(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags,
+                seq, lastSyncSeqId, outFrames, outMergedConfiguration, outSurfaceControl,
+                outInsetsState, outActiveControls);
+    }
+
+    private int relayoutInner(IWindow window, WindowManager.LayoutParams inAttrs,
+            int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+            int lastSyncSeqId, ClientWindowFrames outFrames,
+            MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
+            InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls) {
         final State state;
         synchronized (this) {
             state = mStateForWindow.get(window.asBinder());
@@ -439,10 +476,10 @@
     public void relayoutAsync(IWindow window, WindowManager.LayoutParams inAttrs,
             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
             int lastSyncSeqId) {
-        relayout(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
+        relayoutInner(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
                 lastSyncSeqId, null /* outFrames */, null /* outMergedConfiguration */,
                 null /* outSurfaceControl */, null /* outInsetsState */,
-                null /* outActiveControls */, null /* outSyncSeqIdBundle */);
+                null /* outActiveControls */);
     }
 
     @Override
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 33b29c7..16e1415 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -59,3 +59,14 @@
     bug: "333752000"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "add_schandle_to_vri_surface"
+    namespace: "window_surfaces"
+    description: "Add surface control handle to VRI Surface"
+    bug: "320706287"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index e5658e6..29bb32e 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -52,11 +52,9 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.GraphicBuffer;
-import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.hardware.HardwareBuffer;
 import android.os.IBinder;
 import android.util.Log;
@@ -98,11 +96,6 @@
             | FLAG_SECURE
             | FLAG_DIM_BEHIND;
 
-    private static final RectF sTmpSnapshotSize = new RectF();
-    private static final RectF sTmpDstFrame = new RectF();
-
-    private static final Matrix sSnapshotMatrix = new Matrix();
-    private static final float[] sTmpFloat9 = new float[9];
     private static final Paint sBackgroundPaint = new Paint();
 
     /**
@@ -116,24 +109,27 @@
         private final CharSequence mTitle;
 
         private SystemBarBackgroundPainter mSystemBarBackgroundPainter;
-        private final Rect mTaskBounds;
         private final Rect mFrame = new Rect();
         private final Rect mSystemBarInsets = new Rect();
+        private final int mSnapshotW;
+        private final int mSnapshotH;
         private boolean mSizeMismatch;
 
         public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot,
-                CharSequence title,
-                Rect taskBounds) {
+                CharSequence title) {
             mRootSurface = rootSurface;
             mSnapshot = snapshot;
             mTitle = title;
-            mTaskBounds = taskBounds;
+            final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer();
+            mSnapshotW = hwBuffer.getWidth();
+            mSnapshotH = hwBuffer.getHeight();
         }
 
         /**
          * Initiate system bar painter to draw the system bar background.
          */
-        void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
+        @VisibleForTesting
+        public void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
                 int appearance, ActivityManager.TaskDescription taskDescription,
                 @WindowInsets.Type.InsetsType int requestedVisibleTypes) {
             mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
@@ -143,14 +139,13 @@
         }
 
         /**
-         * Set frame size.
+         * Set frame size that the snapshot should fill. It is the bounds of a task or activity.
          */
-        void setFrames(Rect frame, Rect systemBarInsets) {
+        @VisibleForTesting
+        public void setFrames(Rect frame, Rect systemBarInsets) {
             mFrame.set(frame);
             mSystemBarInsets.set(systemBarInsets);
-            final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
-            mSizeMismatch = (mFrame.width() != snapshot.getWidth()
-                    || mFrame.height() != snapshot.getHeight());
+            mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
             mSystemBarBackgroundPainter.setInsets(systemBarInsets);
         }
 
@@ -186,7 +181,7 @@
 
             // We consider nearly matched dimensions as there can be rounding errors and the user
             // won't notice very minute differences from scaling one dimension more than the other
-            boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
+            boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH);
 
             // Keep a reference to it such that it doesn't get destroyed when finalized.
             SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
@@ -198,12 +193,14 @@
                     .build();
 
             final Rect frame;
+            final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
+            float offsetX = letterboxInsets.left;
+            float offsetY = letterboxInsets.top;
             // We can just show the surface here as it will still be hidden as the parent is
             // still hidden.
             mTransaction.show(childSurfaceControl);
             if (aspectRatioMismatch) {
                 Rect crop = null;
-                final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
                 if (letterboxInsets.left != 0 || letterboxInsets.top != 0
                         || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
                     // Clip off letterbox.
@@ -214,23 +211,27 @@
                 // if letterbox doesn't match window frame, try crop by content insets
                 if (aspectRatioMismatch) {
                     // Clip off ugly navigation bar.
-                    crop = calculateSnapshotCrop(mSnapshot.getContentInsets());
+                    final Rect contentInsets = mSnapshot.getContentInsets();
+                    crop = calculateSnapshotCrop(contentInsets);
+                    offsetX = contentInsets.left;
+                    offsetY = contentInsets.top;
                 }
                 frame = calculateSnapshotFrame(crop);
-                mTransaction.setWindowCrop(childSurfaceControl, crop);
-                mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
-                sTmpSnapshotSize.set(crop);
-                sTmpDstFrame.set(frame);
+                mTransaction.setCrop(childSurfaceControl, crop);
             } else {
                 frame = null;
-                sTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
-                sTmpDstFrame.set(mFrame);
-                sTmpDstFrame.offsetTo(0, 0);
             }
 
-            // Scale the mismatch dimensions to fill the task bounds
-            sSnapshotMatrix.setRectToRect(sTmpSnapshotSize, sTmpDstFrame, Matrix.ScaleToFit.FILL);
-            mTransaction.setMatrix(childSurfaceControl, sSnapshotMatrix, sTmpFloat9);
+            // Align the snapshot with content area.
+            if (offsetX != 0f || offsetY != 0f) {
+                mTransaction.setPosition(childSurfaceControl,
+                        -offsetX * mFrame.width() / mSnapshot.getTaskSize().x,
+                        -offsetY * mFrame.height() / mSnapshot.getTaskSize().y);
+            }
+            // Scale the mismatch dimensions to fill the target frame.
+            final float scaleX = (float) mFrame.width() / mSnapshotW;
+            final float scaleY = (float) mFrame.height() / mSnapshotH;
+            mTransaction.setScale(childSurfaceControl, scaleX, scaleY);
             mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
             mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
 
@@ -261,17 +262,17 @@
          * @param insets Content insets or Letterbox insets
          * @return crop rect in snapshot coordinate space.
          */
-        Rect calculateSnapshotCrop(@NonNull Rect insets) {
+        @VisibleForTesting
+        public Rect calculateSnapshotCrop(@NonNull Rect insets) {
             final Rect rect = new Rect();
-            final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
-            rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
+            rect.set(0, 0, mSnapshotW, mSnapshotH);
 
-            final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
-            final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
+            final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
+            final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
 
             // Let's remove all system decorations except the status bar, but only if the task is at
             // the very top of the screen.
-            final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
+            final boolean isTop = mFrame.top == 0;
             rect.inset((int) (insets.left * scaleX),
                     isTop ? 0 : (int) (insets.top * scaleY),
                     (int) (insets.right * scaleX),
@@ -284,10 +285,10 @@
          *
          * @param crop rect that is in snapshot coordinate space.
          */
-        Rect calculateSnapshotFrame(Rect crop) {
-            final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
-            final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
-            final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
+        @VisibleForTesting
+        public Rect calculateSnapshotFrame(Rect crop) {
+            final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
+            final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
 
             // Rescale the frame from snapshot to window coordinate space
             final Rect frame = new Rect(0, 0,
@@ -303,7 +304,8 @@
         /**
          * Draw status bar and navigation bar background.
          */
-        void drawBackgroundAndBars(Canvas c, Rect frame) {
+        @VisibleForTesting
+        public void drawBackgroundAndBars(Canvas c, Rect frame) {
             final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
             final boolean fillHorizontally = c.getWidth() > frame.right;
             final boolean fillVertically = c.getHeight() > frame.bottom;
@@ -320,33 +322,27 @@
 
         /**
          * Ask system bar background painter to draw status bar background.
-         *
          */
-        void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
+        @VisibleForTesting
+        public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
             mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
                     mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
         }
 
         /**
          * Ask system bar background painter to draw navigation bar background.
-         *
          */
-        void drawNavigationBarBackground(Canvas c) {
+        @VisibleForTesting
+        public void drawNavigationBarBackground(Canvas c) {
             mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
         }
     }
 
-    /**
-     * @return true if the aspect ratio match between a frame and a snapshot buffer.
-     */
-    public static boolean isAspectRatioMatch(Rect frame, TaskSnapshot snapshot) {
+    private static boolean isAspectRatioMatch(Rect frame, int w, int h) {
         if (frame.isEmpty()) {
             return false;
         }
-        final HardwareBuffer buffer = snapshot.getHardwareBuffer();
-        return Math.abs(
-                ((float) buffer.getWidth() / buffer.getHeight())
-                        - ((float) frame.width() / frame.height())) <= 0.01f;
+        return Math.abs(((float) w / h) - ((float) frame.width() / frame.height())) <= 0.01f;
     }
 
     private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
@@ -378,14 +374,14 @@
      */
     public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
             SurfaceControl rootSurface, TaskSnapshot snapshot,
-            Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState,
+            Rect windowBounds, InsetsState topWindowInsetsState,
             boolean releaseAfterDraw) {
         if (windowBounds.isEmpty()) {
             Log.e(TAG, "Unable to draw snapshot on an empty windowBounds");
             return;
         }
         final SnapshotSurface drawSurface = new SnapshotSurface(
-                rootSurface, snapshot, lp.getTitle(), configBounds);
+                rootSurface, snapshot, lp.getTitle());
 
         final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
         final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 260d9a8..72df343 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManager;
 import android.app.TaskInfo;
 import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -79,11 +80,17 @@
 
     /**
      * The {@link TaskInfo} from this task.
-     *  @hide
+     * <p>Note that the configuration of this taskInfo could be from the top activity of its task.
+     * Because only activity contains persisted configuration (e.g. night mode, language). Besides,
+     * it can also be used for activity level snapshot.
      */
     @NonNull
     public ActivityManager.RunningTaskInfo taskInfo;
 
+    /** The bounds of the target task. */
+    @NonNull
+    public final Rect taskBounds = new Rect();
+
     /**
      * The {@link ActivityInfo} of the target activity which to create the starting window.
      * It can be null if the info is the same as the top in task info.
@@ -253,6 +260,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeTypedObject(taskInfo, flags);
+        taskBounds.writeToParcel(dest, flags);
         dest.writeTypedObject(targetActivityInfo, flags);
         dest.writeInt(startingWindowTypeParameter);
         dest.writeTypedObject(topOpaqueWindowInsetsState, flags);
@@ -269,6 +277,7 @@
 
     void readFromParcel(@NonNull Parcel source) {
         taskInfo = source.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+        taskBounds.readFromParcel(source);
         targetActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
         startingWindowTypeParameter = source.readInt();
         topOpaqueWindowInsetsState = source.readTypedObject(InsetsState.CREATOR);
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 001ed79..945164a 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -109,4 +109,15 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    namespace: "windowing_sdk"
+    name: "window_session_relayout_info"
+    description: "Pass an out RelayoutInfo instead of Bundle to fix the Parcel recycle bug"
+    bug: "335601427"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
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/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 7f3b32e..88e6fa3 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -190,15 +190,31 @@
             return;
         }
     }
+    // Calling SystemProperties.set() with a null value is equivalent to an
+    // empty string, but this is not true for the underlying libc function.
+    const char* value_c_str = value ? value->c_str() : "";
+    // Explicitly clear errno so we can recognize __system_property_set()
+    // failures from failed system calls (as opposed to "init rejected your
+    // request" failures).
+    errno = 0;
     bool success;
 #if defined(__BIONIC__)
-    success = !__system_property_set(key.c_str(), value ? value->c_str() : "");
+    success = !__system_property_set(key.c_str(), value_c_str);
 #else
-    success = android::base::SetProperty(key.c_str(), value ? value->c_str() : "");
+    success = android::base::SetProperty(key.c_str(), value_c_str);
 #endif
     if (!success) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                          "failed to set system property (check logcat for reason)");
+        if (errno != 0) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                                 "failed to set system property \"%s\" to \"%s\": %m",
+                                 key.c_str(), value_c_str);
+        } else {
+            // Must have made init unhappy, which will have logged something,
+            // but there's no API to ask for more detail.
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                                 "failed to set system property \"%s\" to \"%s\" (check logcat for reason)",
+                                 key.c_str(), value_c_str);
+        }
     }
 }
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b5256f8..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>
@@ -6973,6 +6974,9 @@
     <!-- Whether to enable left-right split in portrait on this device -->
     <bool name="config_leftRightSplitInPortrait">false</bool>
 
+    <!-- Whether to allow split screen ratios to flexibly calculate based on available space -->
+    <bool name="config_flexibleSplitRatios">false</bool>
+
     <!-- Whether scroll haptic feedback is enabled for rotary encoder scrolls on
          {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER}
          devices. -->
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/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4ea97a5..54c9479 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6452,6 +6452,15 @@
     <!-- Communal profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
     <string name="profile_label_communal">Communal</string>
 
+    <!-- Accessibility label for managed profile user type [CHAR LIMIT=30] -->
+    <string name="accessibility_label_managed_profile">Work profile</string>
+    <!-- Accessibility label for private profile user type [CHAR LIMIT=30] -->
+    <string name="accessibility_label_private_profile">Private space</string>
+    <!-- Accessibility label for clone profile user type [CHAR LIMIT=30] -->
+    <string name="accessibility_label_clone_profile">Clone</string>
+    <!-- Accessibility label for clone profile user type [CHAR LIMIT=30] -->
+    <string name="accessibility_label_communal_profile">Communal</string>
+
     <!-- Notification message used when a notification's normal message contains sensitive information [CHAR_LIMIT=NOTIF_BODY] -->
     <string name="redacted_notification_message">Sensitive notification content hidden</string>
     <!-- Notification action title used instead of a notification's normal title sensitive [CHAR_LIMIT=NOTIF_BODY] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e304027..daf1230 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -408,6 +408,7 @@
   <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
   <java-symbol type="bool" name="config_supportsMultiDisplay" />
   <java-symbol type="bool" name="config_leftRightSplitInPortrait" />
+  <java-symbol type="bool" name="config_flexibleSplitRatios" />
   <java-symbol type="integer" name="config_supportsNonResizableMultiWindow" />
   <java-symbol type="integer" name="config_respectsActivityMinWidthHeightMultiWindow" />
   <java-symbol type="dimen" name="config_minPercentageMultiWindowSupportHeight" />
@@ -1110,6 +1111,10 @@
   <java-symbol type="string" name="profile_label_work_3" />
   <java-symbol type="string" name="profile_label_test" />
   <java-symbol type="string" name="profile_label_communal" />
+  <java-symbol type="string" name="accessibility_label_managed_profile" />
+  <java-symbol type="string" name="accessibility_label_private_profile" />
+  <java-symbol type="string" name="accessibility_label_clone_profile" />
+  <java-symbol type="string" name="accessibility_label_communal_profile" />
   <java-symbol type="string" name="mediasize_unknown_portrait" />
   <java-symbol type="string" name="mediasize_unknown_landscape" />
   <java-symbol type="string" name="mediasize_iso_a0" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 9bb72d9..4d7c009 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -69,6 +69,9 @@
     <!-- Brazil: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000|2652" />
 
+    <!-- Botswana: 1-5 digits (standard system default, not country specific) -->
+    <shortcode country="bw" pattern="\\d{1,5}" free="16641" />
+
     <!-- Belarus: 4 digits -->
     <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
 
@@ -108,7 +111,7 @@
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" />
 
     <!-- Dominican Republic: 1-6 digits (standard system default, not country specific) -->
-    <shortcode country="do" pattern="\\d{1,6}" free="912892" />
+    <shortcode country="do" pattern="\\d{1,6}" free="912892|912" />
 
     <!-- Ecuador: 1-6 digits (standard system default, not country specific) -->
     <shortcode country="ec" pattern="\\d{1,6}" free="466453|18512" />
@@ -141,7 +144,7 @@
     <shortcode country="ge" pattern="\\d{1,5}" premium="801[234]|888[239]" free="95201|95202|95203" />
 
     <!-- Ghana: 4 digits, known premium codes listed -->
-    <shortcode country="gh" pattern="\\d{4}" free="5041|3777" />
+    <shortcode country="gh" pattern="\\d{4}" free="5041|3777|2333" />
 
     <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
     <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" />
@@ -195,7 +198,7 @@
     <shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
 
     <!-- Kuwait: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991|50976" />
+    <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991|50976|7112" />
 
     <!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
     <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399|1324" />
@@ -223,7 +226,7 @@
     <shortcode country="mz" pattern="\\d{1,5}" free="1714" />
 
     <!-- Mexico: 4-7 digits (not confirmed), known premium codes listed -->
-    <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303" />
+    <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303|81811" />
 
     <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668|66966" />
@@ -253,7 +256,7 @@
     <shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
 
     <!-- Pakistan -->
-    <shortcode country="pk" pattern="\\d{1,6}" free="2057|9092|909203" />
+    <shortcode country="pk" pattern="\\d{1,6}" free="2057|9092|909203|909201" />
 
     <!-- Palestine: 5 digits, known premium codes listed -->
     <shortcode country="ps" pattern="\\d{1,5}" free="37477|6681" />
@@ -339,8 +342,8 @@
     <!-- Venezuela: 1-6 digits (standard system default, not country specific) -->
     <shortcode country="ve" pattern="\\d{1,6}" free="538352" />
 
-    <!-- Vietnam: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="vn" pattern="\\d{1,5}" free="5001|9055|8079" />
+    <!-- Vietnam: 1-6 digits (standard system default, not country specific) -->
+    <shortcode country="vn" pattern="\\d{1,6}" free="5001|9055|8079|90002|118989" />
 
     <!-- Mayotte (French Territory): 1-5 digits (not confirmed) -->
     <shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 04e90ba..436ba15 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -116,6 +116,7 @@
     ],
     jni_libs: [
         "libpowermanagertest_jni",
+        "libviewRootImplTest_jni",
         "libworksourceparceltest_jni",
     ],
 
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index 7cc844a..538e7f3 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -67,3 +67,27 @@
     ],
     gtest: false,
 }
+
+cc_test_library {
+    name: "libviewRootImplTest_jni",
+    srcs: [
+        "ViewRootImplTestJni.cpp",
+    ],
+    shared_libs: [
+        "libandroid",
+        "libandroid_runtime_lazy",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libnativehelper",
+        "libpowermanager",
+        "libutils",
+    ],
+    header_libs: ["jni_headers"],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    gtest: false,
+}
diff --git a/core/tests/coretests/jni/ViewRootImplTestJni.cpp b/core/tests/coretests/jni/ViewRootImplTestJni.cpp
new file mode 100644
index 0000000..06a0f23
--- /dev/null
+++ b/core/tests/coretests/jni/ViewRootImplTestJni.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ViewRootImplTest"
+
+#include "jni.h"
+
+#include <android_util_Binder.h>
+#include <binder/IServiceManager.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+#include <android/native_window_jni.h>
+#include <android/surface_control.h>
+#include <android/surface_control_jni.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+namespace android {
+
+static bool nativeCreateASurfaceControlFromSurface(JNIEnv* env, jclass /* obj */,
+        jobject jSurface) {
+    if (!jSurface) {
+        ALOGE("Surface object is null\n");
+        return false;
+    }
+
+    ANativeWindow* window = ANativeWindow_fromSurface(env, jSurface);
+    if (!window) {
+        ALOGE("Could not create ANW from jSurface\n");
+        return false;
+    }
+
+    ASurfaceControl* surfaceControl =
+            ASurfaceControl_createFromWindow(window, "ViewRootImplTestLayer");
+    if (!surfaceControl) {
+        ALOGE("Could not create SC from ANW\n");
+        return false;
+    }
+
+    ANativeWindow_release(window);
+    ASurfaceControl_release(surfaceControl);
+    return true;
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env;
+    const JNINativeMethod methodTable[] = {
+        /* name, signature, funcPtr */
+        { "nativeCreateASurfaceControlFromSurface", "(Landroid/view/Surface;)Z",
+                (void*) nativeCreateASurfaceControlFromSurface },
+    };
+
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+    jniRegisterNativeMethods(env, "android/view/ViewRootImplTest", methodTable,
+                sizeof(methodTable) / sizeof(JNINativeMethod));
+
+    return JNI_VERSION_1_6;
+}
+
+} /* namespace android */
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
new file mode 100644
index 0000000..c00ebe4
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.window.BackEvent.EDGE_LEFT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.platform.test.annotations.Presubmit;
+import android.view.animation.BackGestureInterpolator;
+import android.view.animation.Interpolator;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
+import android.window.BackEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link ImeBackAnimationController}.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:ImeBackAnimationControllerTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ImeBackAnimationControllerTest {
+
+    private static final float PEEK_FRACTION = 0.1f;
+    private static final Interpolator BACK_GESTURE = new BackGestureInterpolator();
+    private static final int IME_HEIGHT = 200;
+    private static final Insets IME_INSETS = Insets.of(0, 0, 0, IME_HEIGHT);
+
+    @Mock
+    private InsetsController mInsetsController;
+    @Mock
+    private WindowInsetsAnimationController mWindowInsetsAnimationController;
+    @Mock
+    private ViewRootInsetsControllerHost mViewRootInsetsControllerHost;
+
+    private ViewRootImpl mViewRoot;
+    private ImeBackAnimationController mBackAnimationController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+            InputMethodManager inputMethodManager = context.getSystemService(
+                    InputMethodManager.class);
+            // cannot mock ViewRootImpl since it's final.
+            mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify());
+            try {
+                mViewRoot.setView(new TextView(context), new WindowManager.LayoutParams(), null);
+            } catch (WindowManager.BadTokenException e) {
+                // activity isn't running, we will ignore BadTokenException.
+            }
+            mBackAnimationController = new ImeBackAnimationController(mViewRoot, mInsetsController);
+
+            when(mWindowInsetsAnimationController.getHiddenStateInsets()).thenReturn(Insets.NONE);
+            when(mWindowInsetsAnimationController.getShownStateInsets()).thenReturn(IME_INSETS);
+            when(mWindowInsetsAnimationController.getCurrentInsets()).thenReturn(IME_INSETS);
+            when(mInsetsController.getHost()).thenReturn(mViewRootInsetsControllerHost);
+            when(mViewRootInsetsControllerHost.getInputMethodManager()).thenReturn(
+                    inputMethodManager);
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
+    public void testAdjustResizeWithAppWindowInsetsListenerPlaysAnim() {
+        // setup ViewRoot with InsetsAnimationCallback and softInputMode=adjustResize
+        mViewRoot.getView()
+                .setWindowInsetsAnimationCallback(mock(WindowInsetsAnimation.Callback.class));
+        mViewRoot.mWindowAttributes.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
+        // start back gesture
+        mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT));
+        // verify that ImeBackAnimationController takes control over IME insets
+        verify(mInsetsController, times(1)).controlWindowInsetsAnimation(anyInt(), any(), any(),
+                anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testAdjustResizeWithoutAppWindowInsetsListenerNotPlayingAnim() {
+        // setup ViewRoot with softInputMode=adjustResize
+        mViewRoot.mWindowAttributes.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
+        // start back gesture
+        mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT));
+        // progress back gesture
+        mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+        // commit back gesture
+        mBackAnimationController.onBackInvoked();
+        // verify that InsetsController#hide is called
+        verify(mInsetsController, times(1)).hide(ime());
+        // verify that ImeBackAnimationController does not take control over IME insets
+        verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
+                anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testAdjustPanScrollsViewRoot() {
+        // simulate view root being panned upwards by 50px
+        int appPan = -50;
+        mViewRoot.setScrollY(appPan);
+        // setup ViewRoot with softInputMode=adjustPan
+        mViewRoot.mWindowAttributes.softInputMode = SOFT_INPUT_ADJUST_PAN;
+
+        // start back gesture
+        WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
+        // simulate ImeBackAnimationController receiving control
+        animationControlListener.onReady(mWindowInsetsAnimationController, ime());
+
+        // progress back gesture
+        float progress = 0.5f;
+        mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
+
+        // verify that view root is scrolled by expected amount
+        float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
+        int expectedViewRootScroll =
+                (int) (appPan * (1 - interpolatedProgress * PEEK_FRACTION));
+        assertEquals(mViewRoot.getScrollY(), expectedViewRootScroll);
+    }
+
+    @Test
+    public void testNewGestureAfterCancelSeamlessTakeover() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // start back gesture
+            WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
+            // simulate ImeBackAnimationController receiving control
+            animationControlListener.onReady(mWindowInsetsAnimationController, ime());
+            // verify initial animation insets are set
+            verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
+                    eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+
+            // progress back gesture
+            mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+
+            // cancel back gesture
+            mBackAnimationController.onBackCancelled();
+            // verify that InsetsController does not notified of a hide-anim (because the gesture
+            // was cancelled)
+            verify(mInsetsController, never()).setPredictiveBackImeHideAnimInProgress(eq(true));
+
+            Mockito.clearInvocations(mWindowInsetsAnimationController);
+            // restart back gesture
+            mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT));
+            // verify that animation controller is reused and initial insets are set immediately
+            verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
+                    eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+        });
+    }
+
+    @Test
+    public void testImeInsetsManipulationCurve() {
+        // start back gesture
+        WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
+        // simulate ImeBackAnimationController receiving control
+        animationControlListener.onReady(mWindowInsetsAnimationController, ime());
+        // verify initial animation insets are set
+        verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
+                eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+
+        Mockito.clearInvocations(mWindowInsetsAnimationController);
+        // progress back gesture
+        float progress = 0.5f;
+        mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
+        // verify correct ime insets manipulation
+        float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
+        int expectedInset =
+                (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
+        verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
+                eq(Insets.of(0, 0, 0, expectedInset)), eq(1f), anyFloat());
+    }
+
+    @Test
+    public void testOnReadyAfterGestureFinished() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // start back gesture
+            WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
+
+            // progress back gesture
+            mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+
+            // commit back gesture
+            mBackAnimationController.onBackInvoked();
+
+            // verify setInsetsAndAlpha never called due onReady delayed
+            verify(mWindowInsetsAnimationController, never()).setInsetsAndAlpha(any(), anyInt(),
+                    anyFloat());
+            verify(mInsetsController, never()).setPredictiveBackImeHideAnimInProgress(eq(true));
+
+            // simulate ImeBackAnimationController receiving control
+            animationControlListener.onReady(mWindowInsetsAnimationController, ime());
+
+            // verify setInsetsAndAlpha immediately called
+            verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
+                    eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+            // verify post-commit hide anim has started
+            verify(mInsetsController, times(1)).setPredictiveBackImeHideAnimInProgress(eq(true));
+        });
+    }
+
+    private WindowInsetsAnimationControlListener startBackGesture() {
+        // start back gesture
+        mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT));
+
+        // verify controlWindowInsetsAnimation is called and capture animationControlListener
+        ArgumentCaptor<WindowInsetsAnimationControlListener> animationControlListener =
+                ArgumentCaptor.forClass(WindowInsetsAnimationControlListener.class);
+        verify(mInsetsController, times(1)).controlWindowInsetsAnimation(anyInt(), any(),
+                animationControlListener.capture(), anyBoolean(), anyLong(), any(), anyInt(),
+                anyBoolean());
+
+        return animationControlListener.getValue();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 4fb85c1f..f05390d 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -21,6 +21,7 @@
 import static android.view.InsetsController.ANIMATION_TYPE_NONE;
 import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
+import static android.view.InsetsController.ANIMATION_TYPE_USER;
 import static android.view.InsetsSource.FLAG_ANIMATE_RESIZING;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
@@ -925,6 +926,95 @@
         });
     }
 
+    @Test
+    public void testImeRequestedVisibleDuringPredictiveBackAnim() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // show ime as initial state
+            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            mController.cancelExistingAnimations(); // fast forward show animation
+            assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+            // start control request (for predictive back animation)
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+                    listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+                    ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+            // Verify that onReady is called (after next predraw)
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+            verify(listener).onReady(notNull(), eq(ime()));
+
+            // verify that insets are requested visible during animation
+            assertTrue(isRequestedVisible(mController, ime()));
+        });
+    }
+
+    @Test
+    public void testImeShowRequestCancelsPredictiveBackPostCommitAnim() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // show ime as initial state
+            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            mController.cancelExistingAnimations(); // fast forward show animation
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+            assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+            // start control request (for predictive back animation)
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+                    listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+                    ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+            // verify that controller
+            // has ANIMATION_TYPE_USER set for ime()
+            assertEquals(ANIMATION_TYPE_USER, mController.getAnimationType(ime()));
+
+            // verify show request is ignored during pre commit phase of predictive back anim
+            mController.show(ime(), true /* fromIme */, null /* statsToken */);
+            assertEquals(ANIMATION_TYPE_USER, mController.getAnimationType(ime()));
+
+            // verify show request is applied during post commit phase of predictive back anim
+            mController.setPredictiveBackImeHideAnimInProgress(true);
+            mController.show(ime(), true /* fromIme */, null /* statsToken */);
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
+
+            // additionally verify that IME ends up visible
+            mController.cancelExistingAnimations();
+            assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+        });
+    }
+
+    @Test
+    public void testImeHideRequestIgnoredDuringPredictiveBackPostCommitAnim() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // show ime as initial state
+            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            mController.cancelExistingAnimations(); // fast forward show animation
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+            assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+            // start control request (for predictive back animation)
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+                    listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+                    ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+            // verify that controller has ANIMATION_TYPE_USER set for ime()
+            assertEquals(ANIMATION_TYPE_USER, mController.getAnimationType(ime()));
+
+            // verify hide request is ignored during post commit phase of predictive back anim
+            // since IME is already animating away
+            mController.setPredictiveBackImeHideAnimInProgress(true);
+            mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+            assertEquals(ANIMATION_TYPE_USER, mController.getAnimationType(ime()));
+        });
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
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 20fb6e3..5caf77d 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+import static android.view.flags.Flags.FLAG_ADD_SCHANDLE_TO_VRI_SURFACE;
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY;
@@ -27,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;
@@ -124,6 +124,10 @@
 
     private CountDownLatch mAfterDrawLatch;
     private Throwable mAfterDrawThrowable;
+    private native boolean nativeCreateASurfaceControlFromSurface(Surface surface);
+    static {
+        System.loadLibrary("viewRootImplTest_jni");
+    }
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -1160,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);
@@ -1364,6 +1349,23 @@
         assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
     }
 
+    @Test
+    @RequiresFlagsEnabled({FLAG_ADD_SCHANDLE_TO_VRI_SURFACE})
+    public void testASurfaceControl_createFromWindow() throws Throwable {
+        mView = new View(sContext);
+        attachViewToWindow(mView);
+        sInstrumentation.runOnMainSync(() -> {
+            mView.setVisibility(View.VISIBLE);
+            mView.invalidate();
+            runAfterDraw(()->{});
+        });
+        waitForAfterDraw();
+        mViewRootImpl = mView.getViewRootImpl();
+        Log.d(TAG, "mViewRootImpl.mSurface=" + mViewRootImpl.mSurface);
+        assertTrue("Could not create ASurfaceControl from VRI surface",
+                nativeCreateASurfaceControlFromSurface(mViewRootImpl.mSurface));
+    }
+
     private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
         try {
             SystemProperties.set(
diff --git a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
index 0361546..6c8dcd3 100644
--- a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
+++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
@@ -77,7 +77,7 @@
                 Color.RED, Color.BLUE);
 
         mSnapshotSurface = new SnapshotDrawerUtils.SnapshotSurface(
-                new SurfaceControl(), snapshot, "Test", taskBounds);
+                new SurfaceControl(), snapshot, "Test");
         mSnapshotSurface.initiateSystemBarPainter(windowFlags, 0, 0,
                 taskDescription, WindowInsets.Type.defaultVisible());
     }
@@ -167,14 +167,16 @@
     @Test
     public void testCalculateSnapshotCrop_taskNotOnTop() {
         final Rect contentInsets = new Rect(0, 10, 0, 10);
-        setupSurface(100, 100, contentInsets, 0, new Rect(0, 50, 100, 150));
+        final Rect bounds = new Rect(0, 50, 100, 150);
+        setupSurface(100, 100, contentInsets, 0, bounds);
+        mSnapshotSurface.setFrames(bounds, contentInsets);
         assertEquals(new Rect(0, 10, 100, 90),
                 mSnapshotSurface.calculateSnapshotCrop(contentInsets));
     }
 
     @Test
     public void testCalculateSnapshotCrop_navBarLeft() {
-        final Rect contentInsets = new Rect(0, 10, 0, 0);
+        final Rect contentInsets = new Rect(10, 0, 0, 0);
         setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
         assertEquals(new Rect(10, 0, 100, 100),
                 mSnapshotSurface.calculateSnapshotCrop(contentInsets));
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/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d295877..98b2431 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
@@ -725,6 +725,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 +2261,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/bubbles/bar/BubbleExpandedViewPinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
index 5d391ec..3b3974d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
@@ -17,8 +17,8 @@
 package com.android.wm.shell.bubbles.bar
 
 import android.content.Context
+import android.graphics.Point
 import android.graphics.Rect
-import android.graphics.RectF
 import android.view.LayoutInflater
 import android.view.View
 import android.widget.FrameLayout
@@ -37,31 +37,25 @@
     private val context: Context,
     private val container: FrameLayout,
     private val positioner: BubblePositioner
-) : BaseBubblePinController() {
+) : BaseBubblePinController({ positioner.availableRect.let { Point(it.width(), it.height()) } }) {
 
     private var dropTargetView: View? = null
     private val tempRect: Rect by lazy(LazyThreadSafetyMode.NONE) { Rect() }
 
-    override fun getScreenCenterX(): Int {
-        return positioner.screenRect.centerX()
+    private val exclRectWidth: Float by lazy {
+        context.resources.getDimension(R.dimen.bubble_bar_dismiss_zone_width)
     }
 
-    override fun getExclusionRect(): RectF {
-        val rect =
-            RectF(
-                0f,
-                0f,
-                context.resources.getDimension(R.dimen.bubble_bar_dismiss_zone_width),
-                context.resources.getDimension(R.dimen.bubble_bar_dismiss_zone_height)
-            )
+    private val exclRectHeight: Float by lazy {
+        context.resources.getDimension(R.dimen.bubble_bar_dismiss_zone_height)
+    }
 
-        val screenRect = positioner.screenRect
-        // Center it around the bottom center of the screen
-        rect.offsetTo(
-            screenRect.exactCenterX() - rect.width() / 2f,
-            screenRect.bottom - rect.height()
-        )
-        return rect
+    override fun getExclusionRectWidth(): Float {
+        return exclRectWidth
+    }
+
+    override fun getExclusionRectHeight(): Float {
+        return exclRectHeight
     }
 
     override fun createDropTargetView(): View {
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 630ad6e..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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.common.bubbles
 
+import android.graphics.Point
 import android.graphics.RectF
 import android.view.View
 import androidx.annotation.VisibleForTesting
@@ -35,7 +36,7 @@
  *
  * Shows a drop target when releasing a view would update the [BubbleBarLocation].
  */
-abstract class BaseBubblePinController {
+abstract class BaseBubblePinController(private val screenSizeProvider: () -> Point) {
 
     private var onLeft = false
     private var dismissZone: RectF? = null
@@ -50,8 +51,8 @@
      */
     fun onDragStart(initialLocationOnLeft: Boolean) {
         onLeft = initialLocationOnLeft
+        screenCenterX = screenSizeProvider.invoke().x / 2
         dismissZone = getExclusionRect()
-        screenCenterX = getScreenCenterX()
     }
 
     /** View has moved to [x] and [y] screen coordinates */
@@ -81,6 +82,7 @@
     fun onDragEnd() {
         getDropTargetView()?.let { view -> view.animateOut { removeDropTargetView(view) } }
         dismissZone = null
+        listener?.onRelease(if (onLeft) LEFT else RIGHT)
     }
 
     /**
@@ -91,11 +93,10 @@
         this.listener = listener
     }
 
-    /** Get screen center coordinate on the x axis. */
-    protected abstract fun getScreenCenterX(): Int
-
-    /** Optional exclusion rect where drag interactions are not processed */
-    protected abstract fun getExclusionRect(): RectF?
+    /** Get width for exclusion rect where dismiss takes over drag */
+    protected abstract fun getExclusionRectWidth(): Float
+    /** Get height for exclusion rect where dismiss takes over drag */
+    protected abstract fun getExclusionRectHeight(): Float
 
     /** Create the drop target view and attach it to the parent */
     protected abstract fun createDropTargetView(): View
@@ -114,6 +115,14 @@
         listener?.onChange(location)
     }
 
+    private fun getExclusionRect(): RectF {
+        val rect = RectF(0f, 0f, getExclusionRectWidth(), getExclusionRectHeight())
+        // Center it around the bottom center of the screen
+        val screenBottom = screenSizeProvider.invoke().y
+        rect.offsetTo(screenCenterX - rect.width() / 2, screenBottom - rect.height())
+        return rect
+    }
+
     private fun showDropTarget(location: BubbleBarLocation) {
         val targetView = getDropTargetView() ?: createDropTargetView().apply { alpha = 0f }
         if (targetView.alpha > 0) {
@@ -162,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/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index f9a286e..bc6ed1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -84,6 +84,8 @@
     private final int mMinimalSizeResizableTask;
     private final int mTaskHeightInMinimizedMode;
     private final float mFixedRatio;
+    /** Allows split ratios to calculated dynamically instead of using {@link #mFixedRatio}. */
+    private final boolean mAllowFlexibleSplitRatios;
     private boolean mIsHorizontalDivision;
 
     /** The first target which is still splitting the screen */
@@ -144,6 +146,8 @@
                 com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
         mMinimalSizeResizableTask = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.default_minimal_size_resizable_task);
+        mAllowFlexibleSplitRatios = res.getBoolean(
+                com.android.internal.R.bool.config_flexibleSplitRatios);
         mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
                 com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
         calculateTargets(isHorizontalDivision, dockSide);
@@ -349,6 +353,9 @@
                 ? mDisplayHeight - mInsets.bottom
                 : mDisplayWidth - mInsets.right;
         int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
+        if (mAllowFlexibleSplitRatios) {
+            size = Math.max(size, mMinimalSizeResizableTask);
+        }
         int topPosition = start + size;
         int bottomPosition = end - size - mDividerSize;
         addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 6834e6d..17121c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -702,10 +702,12 @@
             ShellInit shellInit,
             ShellController shellController,
             Transitions transitions,
+            TaskStackListenerImpl taskStackListener,
             @ShellMainThread Handler mainHandler,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new KeyguardTransitionHandler(
-                    shellInit, shellController, transitions, mainHandler, mainExecutor);
+                shellInit, shellController, transitions, taskStackListener, mainHandler,
+                mainExecutor);
     }
 
     @WMSingleton
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/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 863a51a..9eaf7e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
@@ -44,10 +45,13 @@
 import android.window.IRemoteTransitionFinishedCallback;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -62,7 +66,8 @@
  * <p>This takes the highest priority.
  */
 public class KeyguardTransitionHandler
-        implements Transitions.TransitionHandler, KeyguardChangeListener {
+        implements Transitions.TransitionHandler, KeyguardChangeListener,
+        TaskStackListenerCallback {
     private static final String TAG = "KeyguardTransition";
 
     private final Transitions mTransitions;
@@ -71,6 +76,7 @@
     private final ShellExecutor mMainExecutor;
 
     private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>();
+    private final TaskStackListenerImpl mTaskStackListener;
 
     /**
      * Local IRemoteTransition implementations registered by the keyguard service.
@@ -87,6 +93,8 @@
 
     // Last value reported by {@link KeyguardChangeListener}.
     private boolean mKeyguardShowing = true;
+    @Nullable
+    private WindowContainerToken mDreamToken;
 
     private final class StartedTransition {
         final TransitionInfo mInfo;
@@ -105,18 +113,23 @@
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
             @NonNull Transitions transitions,
+            @NonNull TaskStackListenerImpl taskStackListener,
             @NonNull Handler mainHandler,
             @NonNull ShellExecutor mainExecutor) {
         mTransitions = transitions;
         mShellController = shellController;
         mMainHandler = mainHandler;
         mMainExecutor = mainExecutor;
+        mTaskStackListener = taskStackListener;
         shellInit.addInitCallback(this::onInit, this);
     }
 
     private void onInit() {
         mTransitions.addHandler(this);
         mShellController.addKeyguardChangeListener(this);
+        if (dismissDreamOnKeyguardDismiss()) {
+            mTaskStackListener.addListener(this);
+        }
     }
 
     /**
@@ -142,6 +155,11 @@
     }
 
     @Override
+    public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+        mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null;
+    }
+
+    @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
@@ -271,6 +289,13 @@
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
             @NonNull TransitionRequestInfo request) {
+        if (dismissDreamOnKeyguardDismiss()
+                && (request.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0
+                && mDreamToken != null) {
+            // Dismiss the dream in the same transaction, so that it isn't visible once the device
+            // is unlocked.
+            return new WindowContainerTransaction().removeTask(mDreamToken);
+        }
         return null;
     }
 
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/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index ceac40d..66b3553 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -21,13 +21,14 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
+import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
+
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -43,6 +44,7 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.view.WindowRelayoutResult;
 import android.window.ActivityWindowInfo;
 import android.window.ClientWindowFrames;
 import android.window.SnapshotDrawerUtils;
@@ -100,8 +102,6 @@
             return null;
         }
 
-        final Point taskSize = snapshot.getTaskSize();
-        final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
         final int orientation = snapshot.getOrientation();
         final int displayId = runningTaskInfo.displayId;
 
@@ -139,9 +139,16 @@
         }
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
-            session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
-                    tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
-                    tmpControls, new Bundle());
+            if (windowSessionRelayoutInfo()) {
+                final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(tmpFrames,
+                        tmpMergedConfiguration, surfaceControl, tmpInsetsState, tmpControls);
+                session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
+                        outRelayoutResult);
+            } else {
+                session.relayoutLegacy(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
+                        tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
+                        tmpControls, new Bundle());
+            }
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         } catch (RemoteException e) {
             snapshotSurface.clearWindowSynced();
@@ -150,7 +157,7 @@
         }
 
         SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
-                taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */);
+                info.taskBounds, topWindowInsetsState, true /* releaseAfterDraw */);
         snapshotSurface.mHasDrawn = true;
         snapshotSurface.reportDrawn();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index fed2f34..5c814dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -23,7 +23,6 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.view.Display;
@@ -77,15 +76,13 @@
                 runningTaskInfo.configuration, rootSurface);
         final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost(
                 mContext, display, wlw, "WindowlessSnapshotWindowCreator");
-        final Point taskSize = snapshot.getTaskSize();
-        final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y);
         final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
         final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
         final FrameLayout rootLayout = new FrameLayout(
                 mSplashscreenContentDrawer.createViewContextWrapper(mContext));
         mViewHost.setView(rootLayout, lp);
         SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot,
-                snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
+                windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
 
         final ActivityManager.TaskDescription taskDescription =
                 SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
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/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index cb6495c..8c6bc73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -154,15 +154,10 @@
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                 windowContainerTransactionSupplier, surfaceControlSupplier,
                 surfaceControlViewHostFactory);
-
         mHandler = handler;
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
-
-        Trace.beginSection("DesktopModeWindowDecoration#loadAppInfo");
-        loadAppInfo();
-        Trace.endSection();
     }
 
     void setCaptionListeners(
@@ -245,6 +240,7 @@
                 );
             } else if (mRelayoutParams.mLayoutResId
                     == R.layout.desktop_mode_app_controls_window_decor) {
+                loadAppInfoIfNeeded();
                 mWindowDecorViewHolder = new DesktopModeAppControlsWindowDecorationViewHolder(
                         mResult.mRootView,
                         mOnCaptionTouchListener,
@@ -457,22 +453,31 @@
         return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize();
     }
 
-    private void loadAppInfo() {
-        final ActivityInfo activityInfo = mTaskInfo.topActivityInfo;
-        if (activityInfo == null) {
-            Log.e(TAG, "Top activity info not found in task");
-            return;
+    private void loadAppInfoIfNeeded() {
+        // TODO(b/337370277): move this to another thread.
+        try {
+            Trace.beginSection("DesktopModeWindowDecoration#loadAppInfoIfNeeded");
+            if (mAppIconBitmap != null && mAppName != null) {
+                return;
+            }
+            final ActivityInfo activityInfo = mTaskInfo.topActivityInfo;
+            if (activityInfo == null) {
+                Log.e(TAG, "Top activity info not found in task");
+                return;
+            }
+            PackageManager pm = mContext.getApplicationContext().getPackageManager();
+            final IconProvider provider = new IconProvider(mContext);
+            final Drawable appIconDrawable = provider.getIcon(activityInfo);
+            final Resources resources = mContext.getResources();
+            final BaseIconFactory factory = new BaseIconFactory(mContext,
+                    resources.getDisplayMetrics().densityDpi,
+                    resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
+            mAppIconBitmap = factory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+            final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
+            mAppName = pm.getApplicationLabel(applicationInfo);
+        } finally {
+            Trace.endSection();
         }
-        PackageManager pm = mContext.getApplicationContext().getPackageManager();
-        final IconProvider provider = new IconProvider(mContext);
-        final Drawable appIconDrawable = provider.getIcon(activityInfo);
-        final Resources resources = mContext.getResources();
-        final BaseIconFactory factory = new BaseIconFactory(mContext,
-                resources.getDisplayMetrics().densityDpi,
-                resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
-        mAppIconBitmap = factory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
-        final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
-        mAppName = pm.getApplicationLabel(applicationInfo);
     }
 
     private void closeDragResizeListener() {
@@ -489,6 +494,7 @@
      */
     private void createResizeVeilIfNeeded() {
         if (mResizeVeil != null) return;
+        loadAppInfoIfNeeded();
         mResizeVeil = new ResizeVeil(mContext, mDisplayController, mAppIconBitmap, mTaskInfo,
                 mTaskSurface, mSurfaceControlTransactionSupplier);
     }
@@ -625,6 +631,7 @@
      * Create and display handle menu window.
      */
     void createHandleMenu() {
+        loadAppInfoIfNeeded();
         mHandleMenu = new HandleMenu.Builder(this)
                 .setAppIcon(mAppIconBitmap)
                 .setAppName(mAppName)
@@ -649,10 +656,10 @@
     }
 
     @Override
-    void releaseViews() {
+    void releaseViews(WindowContainerTransaction wct) {
         closeHandleMenu();
         closeMaximizeMenu();
-        super.releaseViews();
+        super.releaseViews(wct);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 54656ff..de6c035 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -18,6 +18,8 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsets.Type.captionBar;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
 import static android.view.WindowInsets.Type.statusBars;
 
 import android.annotation.NonNull;
@@ -41,11 +43,11 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
-import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
 import android.window.SurfaceSyncGroup;
 import android.window.TaskConstants;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -54,7 +56,9 @@
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Supplier;
 
 /**
@@ -131,8 +135,9 @@
     TaskDragResizer mTaskDragResizer;
     private boolean mIsCaptionVisible;
 
+    /** The most recent set of insets applied to this window decoration. */
+    private WindowDecorationInsets mWindowDecorationInsets;
     private final Binder mOwner = new Binder();
-    private final Rect mCaptionInsetsRect = new Rect();
     private final float[] mTmpColor = new float[3];
 
     WindowDecoration(
@@ -203,7 +208,7 @@
         mLayoutResId = params.mLayoutResId;
 
         if (!mTaskInfo.isVisible) {
-            releaseViews();
+            releaseViews(wct);
             finishT.hide(mTaskSurface);
             return;
         }
@@ -226,7 +231,7 @@
                 || mDisplay.getDisplayId() != mTaskInfo.displayId
                 || oldLayoutResId != mLayoutResId
                 || oldNightMode != newNightMode) {
-            releaseViews();
+            releaseViews(wct);
 
             if (!obtainDisplayOrRegisterListener()) {
                 outResult.mRootView = null;
@@ -300,8 +305,8 @@
             // Caption inset is the full width of the task with the |captionHeight| and
             // positioned at the top of the task bounds, also in absolute coordinates.
             // So just reuse the task bounds and adjust the bottom coordinate.
-            mCaptionInsetsRect.set(taskBounds);
-            mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
+            final Rect captionInsetsRect = new Rect(taskBounds);
+            captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
 
             // Caption bounding rectangles: these are optional, and are used to present finer
             // insets than traditional |Insets| to apps about where their content is occluded.
@@ -313,7 +318,7 @@
             } else {
                 // The customizable region can at most be equal to the caption bar.
                 if (params.hasInputFeatureSpy()) {
-                    outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
+                    outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
                 }
                 boundingRects = new Rect[numOfElements];
                 for (int i = 0; i < numOfElements; i++) {
@@ -322,7 +327,7 @@
                     final int elementWidthPx =
                             resources.getDimensionPixelSize(element.mWidthResId);
                     boundingRects[i] =
-                            calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
+                            calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
                     // Subtract the regions used by the caption elements, the rest is
                     // customizable.
                     if (params.hasInputFeatureSpy()) {
@@ -331,18 +336,19 @@
                     }
                 }
             }
-            // Add this caption as an inset source.
-            wct.addInsetsSource(mTaskInfo.token,
-                    mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
-                    boundingRects);
-            wct.addInsetsSource(mTaskInfo.token,
-                    mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
-                    mCaptionInsetsRect, null /* boundingRects */);
+
+            final WindowDecorationInsets newInsets = new WindowDecorationInsets(
+                    mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
+            if (!newInsets.equals(mWindowDecorationInsets)) {
+                // Add or update this caption as an insets source.
+                mWindowDecorationInsets = newInsets;
+                mWindowDecorationInsets.addOrUpdate(wct);
+            }
         } else {
-            wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
-                    WindowInsets.Type.captionBar());
-            wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
-                    WindowInsets.Type.mandatorySystemGestures());
+            if (mWindowDecorationInsets != null) {
+                mWindowDecorationInsets.remove(wct);
+                mWindowDecorationInsets = null;
+            }
         }
 
         // Task surface itself
@@ -398,17 +404,21 @@
         lp.setTrustedOverlay();
         lp.inputFeatures = params.mInputFeatures;
         if (mViewHost == null) {
+            Trace.beginSection("CaptionViewHostLayout-new");
             mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
                     mCaptionWindowManager);
             if (params.mApplyStartTransactionOnDraw) {
                 mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
             }
             mViewHost.setView(outResult.mRootView, lp);
+            Trace.endSection();
         } else {
+            Trace.beginSection("CaptionViewHostLayout-relayout");
             if (params.mApplyStartTransactionOnDraw) {
                 mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
             }
             mViewHost.relayout(lp);
+            Trace.endSection();
         }
         Trace.endSection(); // CaptionViewHostLayout
     }
@@ -481,7 +491,7 @@
         return true;
     }
 
-    void releaseViews() {
+    void releaseViews(WindowContainerTransaction wct) {
         if (mViewHost != null) {
             mViewHost.release();
             mViewHost = null;
@@ -507,19 +517,21 @@
             t.apply();
         }
 
-        final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
-        wct.removeInsetsSource(mTaskInfo.token,
-                mOwner, 0 /* index */, WindowInsets.Type.captionBar());
-        wct.removeInsetsSource(mTaskInfo.token,
-                mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures());
-        mTaskOrganizer.applyTransaction(wct);
+        if (mWindowDecorationInsets != null) {
+            mWindowDecorationInsets.remove(wct);
+            mWindowDecorationInsets = null;
+        }
     }
 
     @Override
     public void close() {
+        Trace.beginSection("WindowDecoration#close");
         mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
-        releaseViews();
+        final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
+        releaseViews(wct);
+        mTaskOrganizer.applyTransaction(wct);
         mTaskSurface.release();
+        Trace.endSection();
     }
 
     static int loadDimensionPixelSize(Resources resources, int resourceId) {
@@ -594,8 +606,12 @@
 
         final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId);
         final Rect captionInsets = new Rect(0, 0, 0, captionHeight);
-        wct.addInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar(),
-                captionInsets, null /* boundingRects */);
+        final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token,
+                mOwner, captionInsets, null /* boundingRets */);
+        if (!newInsets.equals(mWindowDecorationInsets)) {
+            mWindowDecorationInsets = newInsets;
+            mWindowDecorationInsets.addOrUpdate(wct);
+        }
     }
 
     static class RelayoutParams {
@@ -677,6 +693,47 @@
         }
     }
 
+    private static class WindowDecorationInsets {
+        private static final int INDEX = 0;
+        private final WindowContainerToken mToken;
+        private final Binder mOwner;
+        private final Rect mFrame;
+        private final Rect[] mBoundingRects;
+
+        private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame,
+                Rect[] boundingRects) {
+            mToken = token;
+            mOwner = owner;
+            mFrame = frame;
+            mBoundingRects = boundingRects;
+        }
+
+        void addOrUpdate(WindowContainerTransaction wct) {
+            wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects);
+            wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame,
+                    mBoundingRects);
+        }
+
+        void remove(WindowContainerTransaction wct) {
+            wct.removeInsetsSource(mToken, mOwner, INDEX, captionBar());
+            wct.removeInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures());
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false;
+            return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner,
+                    that.mOwner) && Objects.equals(mFrame, that.mFrame)
+                    && Objects.deepEquals(mBoundingRects, that.mBoundingRects);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects));
+        }
+    }
+
     /**
      * Subclass for additional windows associated with this WindowDecoration
      */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 5464937..4eb44d7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -42,6 +42,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.quality.Strictness.LENIENT;
@@ -64,6 +65,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager.LayoutParams;
 import android.window.SurfaceSyncGroup;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
@@ -610,24 +612,25 @@
         mockitoSession.finishMocking();
     }
 
-
     @Test
-    public void testInsetsRemovedWhenCaptionIsHidden() {
+    public void testRelayout_captionHidden_insetsRemoved() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
                 .getDisplay(Display.DEFAULT_DISPLAY);
 
-        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(false);
-
-        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
-                new ActivityManager.TaskDescription.Builder();
         final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                 .setDisplayId(Display.DEFAULT_DISPLAY)
-                .setTaskDescriptionBuilder(taskDescriptionBuilder)
                 .setVisible(true)
+                .setBounds(new Rect(0, 0, 1000, 1000))
                 .build();
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
+        // Run it once so that insets are added.
+        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(true);
+        windowDecor.relayout(taskInfo);
+
+        // Run it again so that insets are removed.
+        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(false);
         windowDecor.relayout(taskInfo);
 
         verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
@@ -637,6 +640,141 @@
     }
 
     @Test
+    public void testRelayout_captionHidden_neverWasVisible_insetsNotRemoved() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true)
+                .setBounds(new Rect(0, 0, 1000, 1000))
+                .build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+        // Hidden from the beginning, so no insets were ever added.
+        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(false);
+        windowDecor.relayout(taskInfo);
+
+        // Never added.
+        verify(mMockWindowContainerTransaction, never()).addInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(captionBar()), any(), any());
+        verify(mMockWindowContainerTransaction, never()).addInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()), any(), any());
+        // No need to remove them if they were never added.
+        verify(mMockWindowContainerTransaction, never()).removeInsetsSource(eq(taskInfo.token),
+                any(), eq(0) /* index */, eq(captionBar()));
+        verify(mMockWindowContainerTransaction, never()).removeInsetsSource(eq(taskInfo.token),
+                any(), eq(0) /* index */, eq(mandatorySystemGestures()));
+    }
+
+    @Test
+    public void testClose_withExistingInsets_insetsRemoved() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true)
+                .setBounds(new Rect(0, 0, 1000, 1000))
+                .build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+        // Relayout will add insets.
+        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(true);
+        windowDecor.relayout(taskInfo);
+        verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(captionBar()), any(), any());
+        verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()), any(), any());
+
+        windowDecor.close();
+
+        // Insets should be removed.
+        verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(captionBar()));
+        verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()));
+    }
+
+    @Test
+    public void testClose_withoutExistingInsets_insetsNotRemoved() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true)
+                .setBounds(new Rect(0, 0, 1000, 1000))
+                .build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+        windowDecor.close();
+
+        // No need to remove insets.
+        verify(mMockWindowContainerTransaction, never()).removeInsetsSource(eq(taskInfo.token),
+                any(), eq(0) /* index */, eq(captionBar()));
+        verify(mMockWindowContainerTransaction, never()).removeInsetsSource(eq(taskInfo.token),
+                any(), eq(0) /* index */, eq(mandatorySystemGestures()));
+    }
+
+    @Test
+    public void testRelayout_captionFrameChanged_insetsReapplied() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(true);
+        final WindowContainerToken token = TestRunningTaskInfoBuilder.createMockWCToken();
+        final TestRunningTaskInfoBuilder builder = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true);
+
+        // Relayout twice with different bounds.
+        final ActivityManager.RunningTaskInfo firstTaskInfo =
+                builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(firstTaskInfo);
+        windowDecor.relayout(firstTaskInfo);
+        final ActivityManager.RunningTaskInfo secondTaskInfo =
+                builder.setToken(token).setBounds(new Rect(50, 50, 1000, 1000)).build();
+        windowDecor.relayout(secondTaskInfo);
+
+        // Insets should be applied twice.
+        verify(mMockWindowContainerTransaction, times(2)).addInsetsSource(eq(token), any(),
+                eq(0) /* index */, eq(captionBar()), any(), any());
+        verify(mMockWindowContainerTransaction, times(2)).addInsetsSource(eq(token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()), any(), any());
+    }
+
+    @Test
+    public void testRelayout_captionFrameUnchanged_insetsNotApplied() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+        mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(true);
+        final WindowContainerToken token = TestRunningTaskInfoBuilder.createMockWCToken();
+        final TestRunningTaskInfoBuilder builder = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true);
+
+        // Relayout twice with the same bounds.
+        final ActivityManager.RunningTaskInfo firstTaskInfo =
+                builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(firstTaskInfo);
+        windowDecor.relayout(firstTaskInfo);
+        final ActivityManager.RunningTaskInfo secondTaskInfo =
+                builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
+        windowDecor.relayout(secondTaskInfo);
+
+        // Insets should only need to be applied once.
+        verify(mMockWindowContainerTransaction, times(1)).addInsetsSource(eq(token), any(),
+                eq(0) /* index */, eq(captionBar()), any(), any());
+        verify(mMockWindowContainerTransaction, times(1)).addInsetsSource(eq(token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()), any(), any());
+    }
+
+    @Test
     public void testTaskPositionAndCropNotSetWhenFalse() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
@@ -763,6 +901,7 @@
 
         void relayout(ActivityManager.RunningTaskInfo taskInfo,
                 boolean applyStartTransactionOnDraw) {
+            mRelayoutParams.mRunningTaskInfo = taskInfo;
             mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
             relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
                     mMockWindowContainerTransaction, mMockView, mRelayoutResult);
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/media/jni/Android.bp b/media/jni/Android.bp
index 8609c4d..8874ff5 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -18,7 +18,10 @@
 cc_library_shared {
     name: "libmedia_jni",
 
-    defaults: ["libcodec2-internal-defaults"],
+    defaults: [
+        "aconfig_lib_cc_static_link.defaults",
+        "libcodec2-internal-defaults",
+    ],
     min_sdk_version: "",
 
     srcs: [
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/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 9e9350b..2e9075c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -186,9 +186,14 @@
         sendBroadcast = sBroadcastOnRestore.contains(name);
         sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
 
-        if (sendBroadcast || sendBroadcastSystemUI) {
+        if (sendBroadcast) {
             // TODO: http://b/22388012
             oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
+        } else if (sendBroadcastSystemUI) {
+            // This is only done for broadcasts sent to system ui as the consumers are known.
+            // It would probably be correct to do it for the ones sent to the system, but consumers
+            // may be depending on the current behavior.
+            oldValue = table.lookup(cr, name, context.getUserId());
         }
 
         try {
@@ -266,7 +271,7 @@
                 if (sendBroadcastSystemUI) {
                     intent.setPackage(
                             context.getString(com.android.internal.R.string.config_systemUi));
-                    context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+                    context.sendBroadcastAsUser(intent, context.getUser(), null);
                 }
             }
         }
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index c87916f..2531454 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -19,6 +19,9 @@
         include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
     },
     static_libs: shell_static_libs,
+    libs: [
+        "device_policy_aconfig_flags_lib",
+    ],
     platform_apis: true,
     certificate: "platform",
     privileged: true,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 5ac0e44..bcfd8f6 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -16,6 +16,7 @@
 
 package com.android.shell;
 
+import static android.app.admin.flags.Flags.onboardingBugreportStorageBugFix;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
@@ -89,10 +90,10 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import com.google.android.collect.Lists;
-
 import libcore.io.Streams;
 
+import com.google.android.collect.Lists;
+
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -109,6 +110,8 @@
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -442,10 +445,14 @@
         }
     }
 
-    private static void sendRemoteBugreportFinishedBroadcast(Context context,
+    private void sendRemoteBugreportFinishedBroadcast(Context context,
             String bugreportFileName, File bugreportFile, long nonce) {
-        cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
-                bugreportFile.getParentFile());
+        // Remote bugreports are stored in the same directory as normal bugreports, meaning that
+        // the remote bugreport storage limit will get applied to normal bugreports whenever a
+        // remote bugreport is triggered. The fix in cleanupOldFiles applies the normal bugreport
+        // limit to the remote bugreports as a quick fix.
+        cleanupOldFiles(
+                REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE, bugreportFile.getParentFile());
         final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
         final Uri bugreportUri = getUri(context, bugreportFile);
         final String bugreportHash = generateFileHash(bugreportFileName);
@@ -496,12 +503,16 @@
         return fileHash;
     }
 
-    static void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
+    void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 try {
-                    FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+                    if (onboardingBugreportStorageBugFix()) {
+                        cleanupOldBugreports();
+                    } else {
+                        FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+                    }
                 } catch (RuntimeException e) {
                     Log.e(TAG, "RuntimeException deleting old files", e);
                 }
@@ -510,6 +521,42 @@
         }.execute();
     }
 
+    private void cleanupOldBugreports() {
+        final File[] files = mBugreportsDir.listFiles();
+        if (files == null) return;
+
+        // Sort with newest files first
+        Arrays.sort(files, new Comparator<File>() {
+            @Override
+            public int compare(File lhs, File rhs) {
+                return Long.compare(rhs.lastModified(), lhs.lastModified());
+            }
+        });
+
+        int normalBugreportFilesCount = 0;
+        int deferredBugreportFilesCount = 0;
+        for (int i = 0; i < files.length; i++) {
+            final File file = files[i];
+
+            // tmp files are deferred bugreports which have their separate storage limit
+            boolean isDeferredBugreportFile = file.getName().endsWith(".tmp");
+            if (isDeferredBugreportFile) {
+                deferredBugreportFilesCount++;
+            } else {
+                normalBugreportFilesCount++;
+            }
+            // Keep files newer than minAgeMs
+            final long age = System.currentTimeMillis() - file.lastModified();
+            final int count = isDeferredBugreportFile
+                    ? deferredBugreportFilesCount : normalBugreportFilesCount;
+            if (count > MIN_KEEP_COUNT  && age > MIN_KEEP_AGE) {
+                if (file.delete()) {
+                    Log.d(TAG, "Deleted old file " + file);
+                }
+            }
+        }
+    }
+
     /**
      * Main thread used to handle all requests but taking screenshots.
      */
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/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/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index e9dc261..20beabb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1063,6 +1063,25 @@
             faceAuthenticateIsCalled()
         }
 
+    @Test
+    fun retryFaceAuthAfterCancel() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+            val isAuthRunning by collectLastValue(underTest.isAuthRunning)
+
+            underTest.requestAuthenticate(FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED)
+            underTest.cancel()
+            clearInvocations(faceManager)
+            underTest.requestAuthenticate(FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED)
+
+            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+            runCurrent()
+
+            assertThat(isAuthRunning).isEqualTo(true)
+            faceAuthenticateIsCalled()
+        }
+
     private suspend fun TestScope.testGatingCheckForFaceAuth(
         gatingCheckModifier: suspend () -> Unit
     ) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt
new file mode 100644
index 0000000..288dc48
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AlternateBouncerToOccludedTransitionViewModelTest : SysuiTestCase() {
+    val kosmos =
+        testKosmos().apply {
+            fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+        }
+
+    private val testScope = kosmos.testScope
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val underTest by lazy { kosmos.alternateBouncerToOccludedTransitionViewModel }
+
+    @Test
+    fun deviceEntryParentViewDisappear() =
+        testScope.runTest {
+            val values by collectValues(underTest.deviceEntryParentViewAlpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    step(0f, TransitionState.STARTED),
+                    step(0f),
+                    step(0.1f),
+                    step(0.2f),
+                    step(0.3f),
+                    step(1f),
+                ),
+                testScope,
+            )
+
+            values.forEach { assertThat(it).isEqualTo(0f) }
+        }
+
+    private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.ALTERNATE_BOUNCER,
+            to = KeyguardState.OCCLUDED,
+            value = value,
+            transitionState = state,
+            ownerName = "AlternateBouncerToOccludedTransitionViewModelTest"
+        )
+    }
+}
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/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index e160cfc..66f7416 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -155,7 +155,6 @@
                 mColorExtractor,
                 mDumpManager,
                 mKeyguardStateController,
-                mKosmos.getScreenOffAnimationController(),
                 mAuthController,
                 mKosmos::getShadeInteractor,
                 mShadeWindowLogger,
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/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 87b473f..6e1b670 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -74,6 +74,7 @@
  * Contains logic that deals with showing a rotate suggestion button with animation.
  */
 public class RotationButtonController {
+    public static final boolean DEBUG_ROTATION = false;
 
     private static final String TAG = "RotationButtonController";
     private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
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/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index f1ab4d1..ba45a51 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -518,9 +518,12 @@
 
     private fun onFaceAuthRequestCompleted() {
         cancelNotReceivedHandlerJob?.cancel()
-        cancellationInProgress.value = false
         _isAuthRunning.value = false
         authCancellationSignal = null
+        // Updates to "cancellationInProgress" may re-trigger face auth
+        // (see processPendingAuthRequests()), so we must update this after setting _isAuthRunning
+        // to false.
+        cancellationInProgress.value = false
     }
 
     private val detectionCallback =
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/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index ae163ea..674c128 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -89,14 +90,14 @@
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.transition.Transitions;
 
-import kotlinx.coroutines.CoroutineScope;
-
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.WeakHashMap;
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.CoroutineScope;
+
 public class KeyguardService extends Service {
     static final String TAG = "KeyguardService";
     static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
@@ -223,6 +224,20 @@
                 initAlphaForAnimationTargets(t, apps);
                 initAlphaForAnimationTargets(t, wallpapers);
 
+                // If the keyguard is going away, hide the dream if one exists.
+                if (dismissDreamOnKeyguardDismiss()
+                        && (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
+                    for (RemoteAnimationTarget app : apps) {
+                        final boolean isDream = app.taskInfo != null
+                                && app.taskInfo.getActivityType()
+                                == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+                        if (isDream && app.mode == RemoteAnimationTarget.MODE_CLOSING) {
+                            t.hide(app.leash);
+                            break;
+                        }
+                    }
+                }
+
                 t.apply();
 
                 runner.onAnimationStart(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 46aec25..5d31d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -21,6 +21,7 @@
 import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
 import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED;
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
@@ -3092,13 +3093,22 @@
                         createInteractionJankMonitorConf(
                                 CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel"));
 
+                // Filter out any closing apps, such as the dream.
+                RemoteAnimationTarget[] openingApps = apps;
+                if (dismissDreamOnKeyguardDismiss()) {
+                    openingApps = Arrays.stream(apps)
+                            .filter(a -> a.mode == RemoteAnimationTarget.MODE_OPENING)
+                            .toArray(RemoteAnimationTarget[]::new);
+                }
+
                 // Pass the surface and metadata to the unlock animation controller.
                 RemoteAnimationTarget[] openingWallpapers = Arrays.stream(wallpapers).filter(
                         w -> w.mode == RemoteAnimationTarget.MODE_OPENING).toArray(
                         RemoteAnimationTarget[]::new);
+
                 mKeyguardUnlockAnimationControllerLazy.get()
                         .notifyStartSurfaceBehindRemoteAnimation(
-                                apps, openingWallpapers, startTime,
+                                openingApps, openingWallpapers, startTime,
                                 mSurfaceBehindRemoteAnimationRequested);
             } else {
                 mInteractionJankMonitor.begin(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index e441017..5a28f711 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -193,5 +193,6 @@
         val TO_AOD_DURATION = TRANSITION_DURATION_MS
         val TO_PRIMARY_BOUNCER_DURATION = TRANSITION_DURATION_MS
         val TO_DOZING_DURATION = TRANSITION_DURATION_MS
+        val TO_OCCLUDED_DURATION = TRANSITION_DURATION_MS
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 3b21141..a8e9041 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -18,6 +18,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
@@ -71,6 +72,12 @@
 
     @Binds
     @IntoSet
+    abstract fun alternateBouncerToOccluded(
+        impl: AlternateBouncerToOccludedTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
     abstract fun alternateBouncerToPrimaryBouncer(
         impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
     ): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
new file mode 100644
index 0000000..27febd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down ALTERNATE_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class AlternateBouncerToOccludedTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        animationFlow.setup(
+            duration = TO_OCCLUDED_DURATION,
+            from = ALTERNATE_BOUNCER,
+            to = KeyguardState.OCCLUDED,
+        )
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 1a2ae8a..906ebad 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -39,6 +39,7 @@
 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import static com.android.systemui.shared.rotation.RotationButtonController.DEBUG_ROTATION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -121,7 +122,6 @@
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
 import com.android.systemui.navigationbar.buttons.DeadZone;
 import com.android.systemui.navigationbar.buttons.KeyButtonView;
-import com.android.systemui.navigationbar.buttons.RotationContextButton;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -135,7 +135,6 @@
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.rotation.RotationButton;
 import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -658,8 +657,8 @@
             // When in gestural and the IME is showing, don't use the nearest region since it will
             // take gesture space away from the IME
             info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */,
-                    false /* inScreen */, false /* useNearestRegion */));
+            info.touchableRegion.set(
+                    getButtonLocations(false /* inScreen */, false /* useNearestRegion */));
         };
 
         mRegionSamplingHelper = new RegionSamplingHelper(mView,
@@ -795,10 +794,7 @@
         repositionNavigationBar(mCurrentRotation);
         mView.setUpdateActiveTouchRegionsCallback(
                 () -> mOverviewProxyService.onActiveNavBarRegionChanges(
-                        getButtonLocations(
-                                true /* includeFloatingButtons */,
-                                true /* inScreen */,
-                                true /* useNearestRegion */)));
+                        getButtonLocations(true /* inScreen */, true /* useNearestRegion */)));
 
         mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
                 mOnComputeInternalInsetsListener);
@@ -1135,16 +1131,14 @@
                 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
         final RotationButtonController rotationButtonController =
                 mView.getRotationButtonController();
-        final RotationButton rotationButton = rotationButtonController.getRotationButton();
-
-        if (RotationContextButton.DEBUG_ROTATION) {
+        if (DEBUG_ROTATION) {
             Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
                     + ", isValid=" + isValid + ", mNavBarWindowState="
                     + StatusBarManager.windowStateToString(mNavigationBarWindowState)
                     + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
-                    + ", isRotateButtonVisible=" + rotationButton.isVisible());
+                    + ", isRotateButtonVisible="
+                    + rotationButtonController.getRotationButton().isVisible());
         }
-
         // Respect the disabled flag, no need for action as flag change callback will handle hiding
         if (rotateSuggestionsDisabled) return;
 
@@ -1903,14 +1897,11 @@
     }
 
     /**
-     * @param includeFloatingButtons Whether to include the floating rotation and overlay button in
-     *                               the region for all the buttons
      * @param inScreenSpace Whether to return values in screen space or window space
      * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
      * @return
      */
-    Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace,
-            boolean useNearestRegion) {
+    Region getButtonLocations(boolean inScreenSpace, boolean useNearestRegion) {
         if (useNearestRegion && !inScreenSpace) {
             // We currently don't support getting the nearest region in anything but screen space
             useNearestRegion = false;
@@ -1928,13 +1919,10 @@
         updateButtonLocation(
                 region, touchRegionCache, mView.getAccessibilityButton(), inScreenSpace,
                 useNearestRegion);
-        if (includeFloatingButtons && mView.getFloatingRotationButton().isVisible()) {
+        if (mView.getFloatingRotationButton().isVisible()) {
             // Note: this button is floating so the nearest region doesn't apply
             updateButtonLocation(
                     region, mView.getFloatingRotationButton().getCurrentView(), inScreenSpace);
-        } else {
-            updateButtonLocation(region, touchRegionCache, mView.getRotateSuggestionButton(),
-                    inScreenSpace, useNearestRegion);
         }
         return region;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 3c69ed9..a73275c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -69,7 +69,6 @@
 import com.android.systemui.navigationbar.buttons.DeadZone;
 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
 import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
-import com.android.systemui.navigationbar.buttons.RotationContextButton;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.res.R;
@@ -153,7 +152,6 @@
     private ShadeViewController mShadeViewController;
     @Nullable
     private PanelExpansionInteractor mPanelExpansionInteractor;
-    private RotationContextButton mRotationContextButton;
     private FloatingRotationButton mFloatingRotationButton;
     private RotationButtonController mRotationButtonController;
 
@@ -293,8 +291,6 @@
                         R.drawable.ic_sysbar_accessibility_button);
         mContextualButtonGroup.addButton(imeSwitcherButton);
         mContextualButtonGroup.addButton(accessibilityButton);
-        mRotationContextButton = new RotationContextButton(R.id.rotate_suggestion,
-                mLightContext, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
         mFloatingRotationButton = new FloatingRotationButton(mContext,
                 R.string.accessibility_rotate_button,
                 R.layout.rotate_suggestion,
@@ -433,10 +429,6 @@
         return mButtonDispatchers.get(R.id.accessibility_button);
     }
 
-    public RotationContextButton getRotateSuggestionButton() {
-        return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion);
-    }
-
     public ButtonDispatcher getHomeHandle() {
         return mButtonDispatchers.get(R.id.home_handle);
     }
@@ -483,18 +475,8 @@
      * Updates the rotation button based on the current navigation mode.
      */
     void updateRotationButton() {
-        if (isGesturalMode(mNavBarMode)) {
-            mContextualButtonGroup.removeButton(R.id.rotate_suggestion);
-            mButtonDispatchers.remove(R.id.rotate_suggestion);
-            mRotationButtonController.setRotationButton(mFloatingRotationButton,
-                    mRotationButtonListener);
-        } else if (mContextualButtonGroup.getContextButton(R.id.rotate_suggestion) == null) {
-            mContextualButtonGroup.addButton(mRotationContextButton);
-            mButtonDispatchers.put(R.id.rotate_suggestion, mRotationContextButton);
-            mRotationButtonController.setRotationButton(mRotationContextButton,
-                    mRotationButtonListener);
-        }
-        mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
+        mRotationButtonController.setRotationButton(mFloatingRotationButton,
+                mRotationButtonListener);
     }
 
     public KeyButtonDrawable getBackDrawable() {
@@ -1143,7 +1125,6 @@
         dumpButton(pw, "home", getHomeButton());
         dumpButton(pw, "handle", getHomeHandle());
         dumpButton(pw, "rcnt", getRecentsButton());
-        dumpButton(pw, "rota", getRotateSuggestionButton());
         dumpButton(pw, "a11y", getAccessibilityButton());
         dumpButton(pw, "ime", getImeSwitchButton());
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
deleted file mode 100644
index ac014b5..0000000
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.navigationbar.buttons;
-
-import android.annotation.DrawableRes;
-import android.annotation.IdRes;
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.shared.rotation.RotationButton;
-import com.android.systemui.shared.rotation.RotationButtonController;
-
-/** Containing logic for the rotation button in nav bar. */
-public class RotationContextButton extends ContextualButton implements RotationButton {
-    public static final boolean DEBUG_ROTATION = false;
-
-    private RotationButtonController mRotationButtonController;
-
-    /**
-     * @param lightContext the context to use to load the icon resource
-     */
-    public RotationContextButton(@IdRes int buttonResId, Context lightContext,
-            @DrawableRes int iconResId) {
-        super(buttonResId, lightContext, iconResId);
-    }
-
-    @Override
-    public void setRotationButtonController(RotationButtonController rotationButtonController) {
-        mRotationButtonController = rotationButtonController;
-    }
-
-    @Override
-    public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
-        setListener((button, visible) -> {
-            if (updatesCallback != null) {
-                updatesCallback.onVisibilityChanged(visible);
-            }
-        });
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-
-        // Start the rotation animation once it becomes visible
-        final KeyButtonDrawable currentDrawable = getImageDrawable();
-        if (visibility == View.VISIBLE && currentDrawable != null) {
-            currentDrawable.resetAnimation();
-            currentDrawable.startAnimation();
-        }
-    }
-
-    @Override
-    protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
-        return KeyButtonDrawable.create(mRotationButtonController.getContext(),
-                lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
-                false /* shadow */, null /* ovalBackgroundColor */);
-    }
-
-    @Override
-    public boolean acceptRotationProposal() {
-        View currentView = getCurrentView();
-        return currentView != null && currentView.isAttachedToWindow();
-    }
-}
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/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 8b7e11c..4a636d2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -68,7 +68,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -131,7 +130,6 @@
             mCallbacks = new ArrayList<>();
 
     private final SysuiColorExtractor mColorExtractor;
-    private final ScreenOffAnimationController mScreenOffAnimationController;
     /**
      * Layout params would be aggregated and dispatched all at once if this is > 0.
      *
@@ -159,7 +157,6 @@
             SysuiColorExtractor colorExtractor,
             DumpManager dumpManager,
             KeyguardStateController keyguardStateController,
-            ScreenOffAnimationController screenOffAnimationController,
             AuthController authController,
             Lazy<ShadeInteractor> shadeInteractorLazy,
             ShadeWindowLogger logger,
@@ -179,7 +176,6 @@
         mKeyguardBypassController = keyguardBypassController;
         mBackgroundExecutor = backgroundExecutor;
         mColorExtractor = colorExtractor;
-        mScreenOffAnimationController = screenOffAnimationController;
         // prefix with {slow} to make sure this dumps at the END of the critical section.
         dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
         mAuthController = authController;
@@ -441,14 +437,8 @@
     private void applyFocusableFlag(NotificationShadeWindowState state) {
         boolean panelFocusable = state.notificationShadeFocusable && state.shadeOrQsExpanded;
         if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
-                || ENABLE_REMOTE_INPUT && state.remoteInputActive
-                // Make the panel focusable if we're doing the screen off animation, since the light
-                // reveal scrim is drawing in the panel and should consume touch events so that they
-                // don't go to the app behind.
-                || mScreenOffAnimationController.shouldIgnoreKeyguardTouches()) {
-            mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
-            mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        } else if (state.glanceableHubShowing) {
+                || (ENABLE_REMOTE_INPUT && state.remoteInputActive)
+                || state.glanceableHubShowing) {
             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
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/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index acb00d5..90567d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -33,7 +33,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
-import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -636,8 +635,7 @@
             TextView categoryTitle = (TextView) inflater.inflate(
                     R.layout.keyboard_shortcuts_category_title, keyboardShortcutsLayout, false);
             categoryTitle.setText(group.getLabel());
-            categoryTitle.setTextColor(group.isSystemGroup() ? Utils.getColorAccent(mContext) :
-                    ColorStateList.valueOf(mContext.getColor(R.color.ksh_application_group_color)));
+            categoryTitle.setTextColor(Utils.getColorAccent(mContext));
             keyboardShortcutsLayout.addView(categoryTitle);
 
             LinearLayout shortcutContainer = (LinearLayout) inflater.inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 3c68805..3784132 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -549,12 +549,23 @@
             try {
                 final int userId = ActivityTaskManager.getService().getLastResumedActivityUserId();
                 final int iconResId = mUserManager.getUserStatusBarIconResId(userId);
-                // TODO(b/170249807, b/230779281): Handle non-managed-profile String
-                String accessibilityString = getManagedProfileAccessibilityString();
                 mMainExecutor.execute(() -> {
                     final boolean showIcon;
                     if (iconResId != Resources.ID_NULL && (!mKeyguardStateController.isShowing()
                             || mKeyguardStateController.isOccluded())) {
+                        String accessibilityString = "";
+                        if (android.os.Flags.allowPrivateProfile()
+                                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
+                            try {
+                                accessibilityString =
+                                        mUserManager.getProfileAccessibilityString(userId);
+                            } catch (Resources.NotFoundException nfe) {
+                                Log.e(TAG, "Accessibility string not found for userId:"
+                                        + userId);
+                            }
+                        } else {
+                            accessibilityString = getManagedProfileAccessibilityString();
+                        }
                         showIcon = true;
                         mIconController.setIcon(mSlotManagedProfile,
                                 iconResId,
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/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 59f7d61..325e7bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -262,7 +262,6 @@
                 mColorExtractor,
                 mDumpManager,
                 mKeyguardStateController,
-                mScreenOffAnimationController,
                 mAuthController,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
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 db998f3..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
@@ -338,8 +317,6 @@
     @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
-    private ScreenOffAnimationController mScreenOffAnimationController;
-    @Mock
     Transitions mTransitions;
     @Mock
     private Optional<OneHandedController> mOneHandedOptional;
@@ -357,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;
@@ -378,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 {
@@ -404,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,
@@ -491,7 +410,6 @@
                 mColorExtractor,
                 mDumpManager,
                 mKeyguardStateController,
-                mScreenOffAnimationController,
                 mAuthController,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
@@ -2470,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/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..71ad3c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerToOccludedTransitionViewModel by Fixture {
+    AlternateBouncerToOccludedTransitionViewModel(
+        animationFlow = keyguardTransitionAnimationFlow,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index d4b7937..a81ac03 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -56,7 +56,6 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shadeController
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
-import com.android.systemui.statusbar.phone.screenOffAnimationController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
@@ -90,7 +89,6 @@
     val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
     val statusBarStateController by lazy { kosmos.statusBarStateController }
     val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
-    val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
     val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
     val sceneInteractor by lazy { kosmos.sceneInteractor }
     val falsingCollector by lazy { kosmos.falsingCollector }
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/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 0448f6d..e66fe1b 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -49,6 +49,16 @@
 }
 
 flag {
+    name: "do_not_reset_key_event_state"
+    namespace: "accessibility"
+    description: "Don't reset the event stream state when receiving an event without policy flag FLAG_PASS_TO_USER. Just pass it through the pipeline."
+    bug: "331900630"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_a11y_checker_logging"
     namespace: "accessibility"
     description: "Whether to identify and log app a11y issues."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 54e545d..5fb60e7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -347,8 +347,13 @@
         final int eventSource = event.getSource();
         final int displayId = event.getDisplayId();
         if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
-            state.reset();
-            clearEventStreamHandler(displayId, eventSource);
+            if (!Flags.doNotResetKeyEventState()) {
+                state.reset();
+                clearEventStreamHandler(displayId, eventSource);
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Not processing event " + event);
+            }
             super.onInputEvent(event, policyFlags);
             return;
         }
@@ -503,9 +508,15 @@
 
     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
         if (!state.shouldProcessKeyEvent(event)) {
+            if (DEBUG) {
+                Slog.d(TAG, "processKeyEvent: not processing: " + event);
+            }
             super.onInputEvent(event, policyFlags);
             return;
         }
+        if (DEBUG) {
+            Slog.d(TAG, "processKeyEvent: " + event);
+        }
         // Since the display id of KeyEvent always would be -1 and there is only one
         // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of
         // DEFAULT_DISPLAY to handle.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 7979936..1ccc48d 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -777,18 +777,24 @@
                     + ", info:" + mHealthInfo.toString());
         }
 
-        mHandler.post(() -> broadcastBatteryChangedIntent(intent, mBatteryChangedOptions));
+        mHandler.post(() -> broadcastBatteryChangedIntent(mContext,
+                intent, mBatteryChangedOptions));
     }
 
-    private static void broadcastBatteryChangedIntent(Intent intent, Bundle options) {
+    private static void broadcastBatteryChangedIntent(Context context, Intent intent,
+            Bundle options) {
         // TODO (293959093): It is important that SystemUI receives this broadcast as soon as
         // possible. Ideally, it should be using binder callbacks but until then, dispatch this
         // as a foreground broadcast to SystemUI.
         final Intent fgIntent = new Intent(intent);
         fgIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         fgIntent.setPackage(sSystemUiPackage);
-        ActivityManager.broadcastStickyIntent(fgIntent, AppOpsManager.OP_NONE,
-                options, UserHandle.USER_ALL);
+        if (com.android.server.flags.Flags.pkgTargetedBatteryChangedNotSticky()) {
+            context.sendBroadcastAsUser(fgIntent, UserHandle.ALL, null, options);
+        } else {
+            ActivityManager.broadcastStickyIntent(fgIntent, AppOpsManager.OP_NONE,
+                    options, UserHandle.USER_ALL);
+        }
 
         ActivityManager.broadcastStickyIntent(intent, new String[] {sSystemUiPackage},
                 AppOpsManager.OP_NONE, options, UserHandle.USER_ALL);
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..d669c8d 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;
@@ -60,6 +63,8 @@
                 return resetSoundDoseTimeout();
             case "set-volume":
                 return setVolume();
+            case "set-device-volume":
+                return setDeviceVolume();
             case "adj-mute":
                 return adjMute();
             case "adj-unmute":
@@ -97,6 +102,8 @@
         pw.println("    Resets the sound dose timeout used for momentary exposure");
         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");
@@ -257,6 +264,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/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index e43d086..9bbcb0c 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -17,3 +17,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "backstage_power"
+    name: "pkg_targeted_battery_changed_not_sticky"
+    description: "Avoid sending the package targeted BATTERY_CHANGED broadcast as sticky"
+    bug: "336832816"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index a5939e9..a439f16 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -386,7 +386,7 @@
             configs = CarrierConfigManager.getDefaultConfig();
         }
         for (String configKey : configs.keySet()) {
-            if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+            if (configKey != null && configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
                 String key = configKey
                         .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
                         .toUpperCase(Locale.ROOT);
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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bff3d39..9d4ab11 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -266,7 +266,6 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.permission.PermissionManager;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
@@ -313,7 +312,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.IPlatformCompat;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.logging.InstanceId;
@@ -704,7 +702,6 @@
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
     private boolean mLockScreenAllowSecureNotifications = true;
-    boolean mSystemExemptFromDismissal = false;
     final ArrayMap<String, ArrayMap<Integer,
             RemoteCallbackList<ICallNotificationEventCallback>>>
             mCallNotificationEventCallbacks = new ArrayMap<>();
@@ -722,7 +719,6 @@
     private GroupHelper mGroupHelper;
     private int mAutoGroupAtCount;
     private boolean mIsTelevision;
-    private DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener;
     protected NotificationAttentionHelper mAttentionHelper;
 
     private int mWarnRemoteViewsSizeBytes;
@@ -973,18 +969,6 @@
     }
 
     protected void setDefaultAssistantForUser(int userId) {
-        String overrideDefaultAssistantString = DeviceConfig.getProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
-        if (overrideDefaultAssistantString != null) {
-            ArraySet<ComponentName> approved = mAssistants.queryPackageForServices(
-                    overrideDefaultAssistantString,
-                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                    userId);
-            for (int i = 0; i < approved.size(); i++) {
-                if (allowAssistant(userId, approved.valueAt(i))) return;
-            }
-        }
         ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
         // We should have only one default assistant by default
         // allowAssistant should execute once in practice
@@ -2670,10 +2654,6 @@
         mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
         mAppOps.stopWatchingMode(mAppOpsListener);
         mAlarmManager.cancelAll();
-
-        if (mDeviceConfigChangedListener != null) {
-            DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
-        }
     }
 
     protected String[] getStringArrayResource(int key) {
@@ -2744,27 +2724,6 @@
         publishLocalService(NotificationManagerInternal.class, mInternalService);
     }
 
-    void registerDeviceConfigChange() {
-        mDeviceConfigChangedListener = properties -> {
-            if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
-                return;
-            }
-            for (String name : properties.getKeyset()) {
-                if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
-                    mAssistants.resetDefaultAssistantsIfNecessary();
-                }
-            }
-        };
-        mSystemExemptFromDismissal = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
-                /* name= */ "application_exemptions",
-                /* defaultValue= */ true);
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                new HandlerExecutor(mHandler),
-                mDeviceConfigChangedListener);
-    }
-
     private void registerNotificationPreferencesPullers() {
         mPullAtomCallback = new StatsPullAtomCallbackImpl();
         mStatsManager.setPullAtomCallback(
@@ -2938,7 +2897,6 @@
             mAssistants.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
             mHistoryManager.onBootPhaseAppsCanStart();
-            registerDeviceConfigChange();
             migrateDefaultNAS();
             maybeShowInitialReviewPermissionsNotification();
 
@@ -7738,7 +7696,7 @@
             return true;
         }
         // Check if an app has been given system exemption
-        return mSystemExemptFromDismissal && mAppOps.checkOpNoThrow(
+        return mAppOps.checkOpNoThrow(
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
                 ai.packageName) == MODE_ALLOWED;
     }
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/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 278deb8..369029ad 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -966,7 +966,14 @@
         if (packageRemovedInfo.mIsAppIdRemoved) {
             // If a system app's updates are uninstalled the UID is not actually removed. Some
             // services need to know the package name affected.
-            if (isReplace) {
+            //
+            // When setting Intent.EXTRA_REPLACING is true for isReplace or isArchived above,
+            // the system triggers AppOpsService#resetAllModes in
+            // ActivityManagerService#broadcastIntentLockedTraced when the action is
+            // ACTION_UID_REMOVED. Add Intent.EXTRA_PACKAGE_NAME for isReplace or isArchived too.
+            // Because AppOpsService#resetAllModes needs the packageName to define which uid to be
+            // reset. If there is no package name, it resets the all appOps for all uids.
+            if (isReplace || isArchived) {
                 extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
             }
 
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 9bdf613..dec97fb 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -284,7 +284,7 @@
         }
 
         String currentLauncherPackageName = getCurrentLauncherPackageName(getParentUserId(userId));
-        if ((currentLauncherPackageName == null || !callerPackageName.equals(
+        if ((currentLauncherPackageName == null || !TextUtils.equals(callerPackageName,
                 currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
             // TODO(b/311619990): Remove dependency on SHELL_UID for testing
             Slog.e(TAG, TextUtils.formatSimple(
@@ -830,7 +830,7 @@
                 mPm.mInstallerService.getAllSessions(userId).getList();
         for (int idx = 0; idx < activeSessions.size(); idx++) {
             PackageInstaller.SessionInfo activeSession = activeSessions.get(idx);
-            if (activeSession.appPackageName.equals(packageName)
+            if (TextUtils.equals(activeSession.appPackageName, packageName)
                     && activeSession.userId == userId && activeSession.active
                     && activeSession.isUnarchival()) {
                 return activeSession;
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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bb6708a..3c702b4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2314,6 +2314,18 @@
         return userTypeDetails.getLabel(userIndex);
     }
 
+    @Override
+    public @NonNull int getProfileAccessibilityLabelResId(@UserIdInt int userId) {
+        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+                "getProfileAccessibilityLabelResId");
+        final UserInfo userInfo = getUserInfoNoChecks(userId);
+        final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+        if (userInfo == null || userTypeDetails == null) {
+            return Resources.ID_NULL;
+        }
+        return userTypeDetails.getAccessibilityString();
+    }
+
     public boolean isProfile(@UserIdInt int userId) {
         checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
         return isProfileUnchecked(userId);
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 56c400a0..c6f36bf 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -127,7 +127,7 @@
     private @DrawableRes final int mStatusBarIcon;
 
     /**
-     * Resource ID ({@link StringRes}) of the of the labels to describe badged apps; should be the
+     * Resource ID ({@link StringRes}) of the labels to describe badged apps; should be the
      * same format as com.android.internal.R.color.profile_badge_1. These are used for accessibility
      * services.
      *
@@ -161,6 +161,12 @@
     private final @Nullable int[] mDarkThemeBadgeColors;
 
     /**
+     * Resource ID ({@link StringRes}) of the accessibility string that describes the user type.
+     * This is used by accessibility services like Talkback.
+     */
+    private final @Nullable int mAccessibilityString;
+
+    /**
      * The default {@link UserProperties} for the user type.
      * <p> The uninitialized value of each property is implied by {@link UserProperties.Builder}.
      */
@@ -177,6 +183,7 @@
             @Nullable Bundle defaultSystemSettings,
             @Nullable Bundle defaultSecureSettings,
             @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
+            @Nullable int accessibilityString,
             @NonNull UserProperties defaultUserProperties) {
         this.mName = name;
         this.mEnabled = enabled;
@@ -196,6 +203,7 @@
         this.mBadgeLabels = badgeLabels;
         this.mBadgeColors = badgeColors;
         this.mDarkThemeBadgeColors = darkThemeBadgeColors;
+        this.mAccessibilityString = accessibilityString;
         this.mDefaultUserProperties = defaultUserProperties;
     }
 
@@ -323,6 +331,10 @@
         return mDefaultUserProperties;
     }
 
+    public @StringRes int getAccessibilityString() {
+        return mAccessibilityString;
+    }
+
     public boolean isProfile() {
         return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
     }
@@ -430,6 +442,7 @@
         private @DrawableRes int mBadgePlain = Resources.ID_NULL;
         private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
         private @DrawableRes int mStatusBarIcon = Resources.ID_NULL;
+        private @StringRes int mAccessibilityString = Resources.ID_NULL;
         // Default UserProperties cannot be null but for efficiency we don't initialize it now.
         // If it isn't set explicitly, {@link UserProperties.Builder#build()} will be used.
         private @Nullable UserProperties mDefaultUserProperties = null;
@@ -529,6 +542,14 @@
         }
 
         /**
+         * Sets the accessibility label associated with the user
+         */
+        public Builder setAccessibilityString(@StringRes int accessibilityString) {
+            mAccessibilityString = accessibilityString;
+            return this;
+        }
+
+        /**
          * Sets (replacing if necessary) the default UserProperties object for this user type.
          * Takes a builder, rather than a built object, to efficiently ensure that a fresh copy of
          * properties is stored (since it later might be modified by UserProperties#updateFromXml).
@@ -589,6 +610,7 @@
                     mDefaultSystemSettings,
                     mDefaultSecureSettings,
                     mDefaultCrossProfileIntentFilters,
+                    mAccessibilityString,
                     getDefaultUserProperties());
         }
 
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1e7fdfe..2715025 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -140,6 +140,8 @@
                         com.android.internal.R.color.system_neutral2_800)
                 .setDarkThemeBadgeColors(
                         com.android.internal.R.color.system_neutral2_900)
+                .setAccessibilityString(com.android.internal
+                        .R.string.accessibility_label_clone_profile)
                 .setDefaultRestrictions(null)
                 .setDefaultCrossProfileIntentFilters(getDefaultCloneCrossProfileIntentFilter())
                 .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
@@ -195,6 +197,8 @@
                         com.android.internal.R.color.profile_badge_1_dark,
                         com.android.internal.R.color.profile_badge_2_dark,
                         com.android.internal.R.color.profile_badge_3_dark)
+                .setAccessibilityString(com.android.internal
+                        .R.string.accessibility_label_managed_profile)
                 .setDefaultRestrictions(getDefaultProfileRestrictions())
                 .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
                 .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter())
@@ -306,6 +310,8 @@
                         R.color.black)
                 .setDarkThemeBadgeColors(
                         R.color.white)
+                .setAccessibilityString(com.android.internal
+                        .R.string.accessibility_label_private_profile)
                 .setDefaultRestrictions(getDefaultPrivateProfileRestrictions())
                 .setDefaultCrossProfileIntentFilters(getDefaultPrivateCrossProfileIntentFilter())
                 .setDefaultUserProperties(new UserProperties.Builder()
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 42efc2d..1f320da 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4105,6 +4105,7 @@
         boolean removedFromHistory = false;
 
         cleanUp(false /* cleanServices */, false /* setState */);
+        setVisibleRequested(false);
 
         if (hasProcess()) {
             app.removeActivity(this, true /* keepAssociation */);
@@ -8689,6 +8690,15 @@
             // calculate the override, skip the override.
             return;
         }
+        // Make sure the orientation related fields will be updated by the override insets, because
+        // fixed rotation has assigned the fields from display's configuration.
+        if (hasFixedRotationTransform()) {
+            inOutConfig.windowConfiguration.setAppBounds(null);
+            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+            inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+            inOutConfig.orientation = ORIENTATION_UNDEFINED;
+        }
 
         // Override starts here.
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
@@ -8725,8 +8735,7 @@
             // For the case of PIP transition and multi-window environment, the
             // smallestScreenWidthDp is handled already. Override only if the app is in
             // fullscreen.
-            DisplayInfo info = new DisplayInfo();
-            mDisplayContent.getDisplay().getDisplayInfo(info);
+            final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
             mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
                     mDisplayContent.getDisplayMetrics().density,
                     inOutConfig, true /* overrideConfig */);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 4c53e0f..c9703d8 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -302,7 +302,8 @@
                 } else if (prevTask.isActivityTypeHome()) {
                     removedWindowContainer = currentTask;
                     backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
-                    mShowWallpaper = true;
+                    final ActivityRecord ar = prevTask.getTopNonFinishingActivity();
+                    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
@@ -310,7 +311,9 @@
                     // another task.
                     final Task prevParent = prevTask.getParent().asTask();
                     final Task currParent = currentTask.getParent().asTask();
-                    if (prevTask.inMultiWindowMode() && prevParent != currParent) {
+                    if ((prevTask.inMultiWindowMode() && prevParent != currParent)
+                            // Do not animate to translucent task, it could be trampoline.
+                            || hasTranslucentActivity(currentActivity, prevActivities)) {
                         backType = BackNavigationInfo.TYPE_CALLBACK;
                     } else {
                         removedWindowContainer = prevTask;
@@ -525,7 +528,7 @@
         }
         for (int i = prevActivities.size() - 1; i >= 0; --i) {
             final ActivityRecord test = prevActivities.get(i);
-            if (!test.occludesParent() || test.showWallpaper()) {
+            if (!test.occludesParent() || test.hasWallpaper()) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index f04b4af..8069a93 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -48,8 +48,15 @@
     /**
      * Flag to indicate whether to restrict desktop mode to supported devices.
      */
+    @VisibleForTesting
+    static final String ENFORCE_DEVICE_RESTRICTIONS_KEY =
+            "persist.wm.debug.desktop_mode_enforce_device_restrictions";
+
+    /**
+     * Flag to indicate whether to restrict desktop mode to supported devices.
+     */
     private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
+            ENFORCE_DEVICE_RESTRICTIONS_KEY, true);
 
     private StringBuilder mLogBuilder;
 
@@ -178,24 +185,24 @@
      * Return {@code true} if desktop mode should be restricted to supported devices.
      */
     @VisibleForTesting
-    public boolean enforceDeviceRestrictions() {
+    static boolean enforceDeviceRestrictions() {
         return ENFORCE_DEVICE_RESTRICTIONS;
     }
 
     /**
      * Return {@code true} if the current device supports desktop mode.
      */
+    // TODO(b/337819319): use a companion object instead.
     @VisibleForTesting
-    public boolean isDesktopModeSupported(@NonNull Context context) {
+    static boolean isDesktopModeSupported(@NonNull Context context) {
         return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
     }
 
     /**
      * Return {@code true} if desktop mode can be entered on the current device.
      */
-    boolean canEnterDesktopMode(@NonNull Context context) {
+    static boolean canEnterDesktopMode(@NonNull Context context) {
         return isDesktopModeEnabled()
                 && (!enforceDeviceRestrictions() || isDesktopModeSupported(context));
     }
-
 }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index f8aa69b..bb86460 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -41,6 +41,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -79,6 +80,7 @@
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager;
+import android.view.WindowRelayoutResult;
 import android.window.ClientWindowFrames;
 import android.window.InputTransferToken;
 import android.window.OnBackInvokedCallbackInfo;
@@ -280,20 +282,29 @@
     @Override
     public int relayout(IWindow window, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+            int lastSyncSeqId, WindowRelayoutResult outRelayoutResult) {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
+        int res = mService.relayoutWindow(this, window, attrs, requestedWidth,
+                requestedHeight, viewFlags, flags, seq, lastSyncSeqId, outRelayoutResult);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        return res;
+    }
+
+    /** @deprecated */
+    @Deprecated
+    @Override
+    public int relayoutLegacy(IWindow window, WindowManager.LayoutParams attrs,
+            int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
             int lastSyncSeqId, ClientWindowFrames outFrames,
             MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl,
             InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
             Bundle outBundle) {
-        if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
-                + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
         int res = mService.relayoutWindow(this, window, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags, seq,
                 lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                 outActiveControls, outBundle);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
-                + Binder.getCallingPid());
         return res;
     }
 
@@ -301,10 +312,15 @@
     public void relayoutAsync(IWindow window, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
             int lastSyncSeqId) {
-        relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
-                lastSyncSeqId, null /* outFrames */, null /* mergedConfiguration */,
-                null /* outSurfaceControl */, null /* outInsetsState */,
-                null /* outActiveControls */, null /* outSyncIdBundle */);
+        if (windowSessionRelayoutInfo()) {
+            relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
+                    lastSyncSeqId, null /* outRelayoutResult */);
+        } else {
+            relayoutLegacy(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
+                    lastSyncSeqId, null /* outFrames */, null /* mergedConfiguration */,
+                    null /* outSurfaceControl */, null /* outInsetsState */,
+                    null /* outActiveControls */, null /* outSyncIdBundle */);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9a7f87d..9b98380 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3691,6 +3691,9 @@
                 info.requestedVisibleTypes = topMainWin.getRequestedVisibleTypes();
             }
         }
+        final Rect rotatedBounds = activity.getFixedRotationTransformDisplayBounds();
+        info.taskBounds.set(rotatedBounds != null ? rotatedBounds
+                : info.taskInfo.configuration.windowConfiguration.getBounds());
         // If the developer has persist a different configuration, we need to override it to the
         // starting window because persisted configuration does not effect to Task.
         info.taskInfo.configuration.setTo(activity.getConfiguration());
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 90ac576..a437914 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3889,6 +3889,18 @@
         return false;
     }
 
+
+    /** @return {@code true} if this container wants to show wallpaper. */
+    boolean hasWallpaper() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            if (child.hasWallpaper()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Nullable
     static WindowContainer fromBinder(IBinder binder) {
         return RemoteToken.fromBinder(binder).getContainer();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 616ab88..b19f0be 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -308,6 +308,7 @@
 import android.view.WindowManager.RemoveContentMode;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.view.WindowRelayoutResult;
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 import android.view.inputmethod.ImeTracker;
@@ -2268,12 +2269,54 @@
         }
     }
 
+    /** Relayouts window. */
+    public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
+            int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
+            int lastSyncSeqId, WindowRelayoutResult outRelayoutResult) {
+        final ClientWindowFrames outFrames;
+        final MergedConfiguration outMergedConfiguration;
+        final SurfaceControl outSurfaceControl;
+        final InsetsState outInsetsState;
+        final InsetsSourceControl.Array outActiveControls;
+        if (outRelayoutResult != null) {
+            outFrames = outRelayoutResult.frames;
+            outMergedConfiguration = outRelayoutResult.mergedConfiguration;
+            outSurfaceControl = outRelayoutResult.surfaceControl;
+            outInsetsState = outRelayoutResult.insetsState;
+            outActiveControls = outRelayoutResult.activeControls;
+        } else {
+            outFrames = null;
+            outMergedConfiguration = null;
+            outSurfaceControl = null;
+            outInsetsState = null;
+            outActiveControls = null;
+        }
+        return relayoutWindowInner(session, client, attrs, requestedWidth, requestedHeight,
+                viewVisibility, flags, seq, lastSyncSeqId, outFrames, outMergedConfiguration,
+                outSurfaceControl, outInsetsState, outActiveControls, null /* outBundle */,
+                outRelayoutResult);
+    }
+
+    /** @deprecated */
+    @Deprecated
     public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
             int lastSyncSeqId, ClientWindowFrames outFrames,
             MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
             InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
             Bundle outBundle) {
+        return relayoutWindowInner(session, client, attrs, requestedWidth, requestedHeight,
+                viewVisibility, flags, seq, lastSyncSeqId, outFrames, outMergedConfiguration,
+                outSurfaceControl, outInsetsState, outActiveControls, outBundle,
+                null /* outRelayoutResult */);
+    }
+
+    private int relayoutWindowInner(Session session, IWindow client, LayoutParams attrs,
+            int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
+            int lastSyncSeqId, ClientWindowFrames outFrames,
+            MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
+            InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
+            Bundle outBundle, WindowRelayoutResult outRelayoutResult) {
         if (outActiveControls != null) {
             outActiveControls.set(null);
         }
@@ -2608,8 +2651,14 @@
             }
 
             if (outFrames != null && outMergedConfiguration != null) {
-                final boolean shouldReportActivityWindowInfo = outBundle != null
-                        && win.mLastReportedActivityWindowInfo != null;
+                final boolean shouldReportActivityWindowInfo;
+                if (Flags.windowSessionRelayoutInfo()) {
+                    shouldReportActivityWindowInfo = outRelayoutResult != null
+                            && win.mLastReportedActivityWindowInfo != null;
+                } else {
+                    shouldReportActivityWindowInfo = outBundle != null
+                            && win.mLastReportedActivityWindowInfo != null;
+                }
                 final ActivityWindowInfo outActivityWindowInfo = shouldReportActivityWindowInfo
                         ? new ActivityWindowInfo()
                         : null;
@@ -2618,8 +2667,13 @@
                         outActivityWindowInfo, false /* useLatestConfig */, shouldRelayout);
 
                 if (shouldReportActivityWindowInfo) {
-                    outBundle.putParcelable(IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO,
-                            outActivityWindowInfo);
+                    if (Flags.windowSessionRelayoutInfo()) {
+                        outRelayoutResult.activityWindowInfo = outActivityWindowInfo;
+                    } else {
+                        outBundle.putParcelable(
+                                IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO,
+                                outActivityWindowInfo);
+                    }
                 }
 
                 // Set resize-handled here because the values are sent back to the client.
@@ -2650,7 +2704,19 @@
                         win.isVisible() /* visible */, false /* removed */);
             }
 
-            if (outBundle != null) {
+            if (Flags.windowSessionRelayoutInfo()) {
+                if (outRelayoutResult != null) {
+                    if (win.syncNextBuffer() && viewVisibility == View.VISIBLE
+                            && win.mSyncSeqId > lastSyncSeqId) {
+                        outRelayoutResult.syncSeqId = win.shouldSyncWithBuffers()
+                                ? win.mSyncSeqId
+                                : -1;
+                        win.markRedrawForSyncReported();
+                    } else {
+                        outRelayoutResult.syncSeqId = -1;
+                    }
+                }
+            } else if (outBundle != null) {
                 final int maybeSyncSeqId;
                 if (win.syncNextBuffer() && viewVisibility == View.VISIBLE
                         && win.mSyncSeqId > lastSyncSeqId) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index d91a211..fd1b5be 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -110,9 +110,9 @@
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
-    private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 50;
+    private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 200;
     private static final long RAPID_ACTIVITY_LAUNCH_MS = 500;
-    private static final long RESET_RAPID_ACTIVITY_LAUNCH_MS = 5 * RAPID_ACTIVITY_LAUNCH_MS;
+    private static final long RESET_RAPID_ACTIVITY_LAUNCH_MS = 3 * RAPID_ACTIVITY_LAUNCH_MS;
 
     public static final int STOPPED_STATE_NOT_STOPPED = 0;
     public static final int STOPPED_STATE_FIRST_LAUNCH = 1;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2fcee50..c25080f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3092,12 +3092,13 @@
     }
 
     void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
+        final int baseType = getBaseType();
         if (mSession.mCanAddInternalSystemWindow
-                || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
+                || (!isSystemAlertWindowType(baseType) && baseType != TYPE_TOAST)) {
             return;
         }
 
-        if (mAttrs.type == TYPE_APPLICATION_OVERLAY && mAttrs.isSystemApplicationOverlay()
+        if (baseType == TYPE_APPLICATION_OVERLAY && mAttrs.isSystemApplicationOverlay()
                 && mSession.mCanCreateSystemApplicationOverlay) {
             return;
         }
@@ -5857,6 +5858,7 @@
         return hasWallpaper();
     }
 
+    @Override
     boolean hasWallpaper() {
         return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || hasWallpaperForLetterboxBackground();
     }
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/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index b91ef7c..37065fd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -237,6 +237,8 @@
         assertThat(mainUserId).isEqualTo(parentProfileInfo.id);
         removeUser(cloneProfileUser.id);
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+        assertThat(mUserManager.getProfileAccessibilityString(cloneProfileUser.id)).isEqualTo(
+                Resources.getSystem().getString(userTypeDetails.getAccessibilityString()));
     }
 
     @Test
@@ -276,6 +278,9 @@
         assertWithMessage("Communal profile not visible").that(umCommunal.isUserVisible()).isTrue();
         switchUser(originalCurrent);
         assertWithMessage("Communal profile not visible").that(umCommunal.isUserVisible()).isTrue();
+        assertThat(mUserManager.getProfileAccessibilityString(communal.getIdentifier()))
+                .isEqualTo(Resources.getSystem()
+                        .getString(userTypeDetails.getAccessibilityString()));
     }
 
     @Test
@@ -372,6 +377,18 @@
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
         assertThat(profileUM.getProfileLabel()).isEqualTo(
                 Resources.getSystem().getString(userTypeDetails.getLabel(0)));
+        assertThat(mUserManager.getProfileAccessibilityString(privateProfileUser.id)).isEqualTo(
+                Resources.getSystem().getString(userTypeDetails.getAccessibilityString()));
+    }
+
+    @Test
+    public void testGetProfileAccessibilityString_throwsExceptionForNonProfileUser() {
+        UserInfo user1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
+        assertThat(user1).isNotNull();
+        assertThrows(Resources.NotFoundException.class,
+                () -> mUserManager.getProfileAccessibilityString(user1.id));
+        assertThrows(Resources.NotFoundException.class,
+                () -> mUserManager.getProfileAccessibilityString(UserHandle.USER_SYSTEM));
     }
 
     @MediumTest
@@ -1003,6 +1020,8 @@
                 "android", 0, asHandle(userId)));
         assertThat(userManagerForUser.isUserOfType(userTypeDetails.getName())).isTrue();
         assertThat(userManagerForUser.isProfile()).isEqualTo(userTypeDetails.isProfile());
+        assertThat(mUserManager.getProfileAccessibilityString(managedProfileUser.id)).isEqualTo(
+                Resources.getSystem().getString(userTypeDetails.getAccessibilityString()));
     }
 
     /** Test that UserManager returns the correct UserProperties for a new managed profile. */
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ce7a0a0..74d8433 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -243,7 +243,6 @@
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.rule.LimitDevicesRule;
-import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
@@ -280,7 +279,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.config.sysui.TestableFlagResolver;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.InstanceIdSequenceFake;
@@ -602,7 +600,9 @@
         when(mContext.getContentResolver()).thenReturn(cr);
         doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt());
 
-        setDpmAppOppsExemptFromDismissal(false);
+        when(mAppOpsManager.checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
+                mPkg)).thenReturn(AppOpsManager.MODE_IGNORED);
 
         // Use this testable looper.
         mTestableLooper = TestableLooper.get(this);
@@ -900,7 +900,6 @@
     @After
     public void tearDown() throws Exception {
         if (mFile != null) mFile.delete();
-        clearDeviceConfig();
 
         if (mActivityIntent != null) {
             mActivityIntent.cancel();
@@ -1200,19 +1199,6 @@
         return answers;
     }
 
-    private void clearDeviceConfig() {
-        DeviceConfig.resetToDefaults(
-                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
-    }
-
-    private void setDefaultAssistantInDeviceConfig(String componentName) {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
-                componentName,
-                false);
-    }
-
     private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
             String groupKey, boolean isSummary, boolean mutable) {
         // Give it a person
@@ -9092,7 +9078,6 @@
 
     @Test
     public void setDefaultAssistantForUser_fromConfigXml() {
-        clearDeviceConfig();
         ComponentName xmlConfig = new ComponentName("config", "xml");
         ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(xmlConfig));
         when(mResources
@@ -9115,51 +9100,6 @@
     }
 
     @Test
-    public void setDefaultAssistantForUser_fromDeviceConfig() {
-        ComponentName xmlConfig = new ComponentName("xml", "config");
-        ComponentName deviceConfig = new ComponentName("device", "config");
-        setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString());
-        when(mResources
-                .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
-                .thenReturn(xmlConfig.flattenToString());
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mAssistants.queryPackageForServices(eq(null), anyInt(), anyInt()))
-                .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
-        when(mAssistants.getDefaultComponents())
-                .thenReturn(new ArraySet<>(Arrays.asList(deviceConfig)));
-        mService.setNotificationAssistantAccessGrantedCallback(
-                mNotificationAssistantAccessGrantedCallback);
-
-        mService.setDefaultAssistantForUser(0);
-
-        verify(mNotificationAssistantAccessGrantedCallback)
-                .onGranted(eq(deviceConfig), eq(0), eq(true), eq(false));
-    }
-
-    @Test
-    public void setDefaultAssistantForUser_deviceConfigInvalid() {
-        ComponentName xmlConfig = new ComponentName("xml", "config");
-        ComponentName deviceConfig = new ComponentName("device", "config");
-        setDefaultAssistantInDeviceConfig(deviceConfig.flattenToString());
-        when(mResources
-                .getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
-                .thenReturn(xmlConfig.flattenToString());
-        when(mContext.getResources()).thenReturn(mResources);
-        // Only xmlConfig is valid, deviceConfig is not.
-        when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0)))
-                .thenReturn(new ArraySet<>(Collections.singleton(xmlConfig)));
-        when(mAssistants.getDefaultComponents())
-                .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
-        mService.setNotificationAssistantAccessGrantedCallback(
-                mNotificationAssistantAccessGrantedCallback);
-
-        mService.setDefaultAssistantForUser(0);
-
-        verify(mNotificationAssistantAccessGrantedCallback)
-                .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false));
-    }
-
-    @Test
     public void clearMultipleDefaultAssistantPackagesShouldEnableOnlyOne() throws RemoteException {
         ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
                 generateResetComponentValues();
@@ -11006,7 +10946,6 @@
         tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, "");
         tr.addOverride(com.android.internal.R.string.config_defaultDndAccessPackages, "");
         tr.addOverride(com.android.internal.R.string.config_defaultAssistantAccessComponent, "");
-        setDefaultAssistantInDeviceConfig("");
 
         mService.loadDefaultApprovedServices(USER_SYSTEM);
 
@@ -13425,7 +13364,6 @@
             throws Exception {
         when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        setDpmAppOppsExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -13451,7 +13389,6 @@
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
                 mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        setDpmAppOppsExemptFromDismissal(true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -13459,8 +13396,8 @@
         // When: fix the notification with NotificationManagerService
         mService.fixNotification(n, mPkg, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
 
-        // Then: the notification's flag FLAG_NO_DISMISS should be cleared
-        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
+        // Then: the notification's flag FLAG_NO_DISMISS should be set
+        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
     }
 
     @Test
@@ -13468,9 +13405,8 @@
             throws Exception {
         when(mAppOpsManager.checkOpNoThrow(
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
-                mPkg)).thenReturn(AppOpsManager.MODE_ALLOWED);
+                mPkg)).thenReturn(AppOpsManager.MODE_IGNORED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        setDpmAppOppsExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -15551,14 +15487,6 @@
                 PendingIntent.FLAG_MUTABLE);
     }
 
-    private void setDpmAppOppsExemptFromDismissal(boolean isOn) {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
-                /* name= */ "application_exemptions",
-                String.valueOf(isOn),
-                /* makeDefault= */ false);
-    }
-
     private void allowTestPackageToToast() throws Exception {
         assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
         mService.isSystemUid = false;
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/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index ab8c53c..a39a1a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -167,8 +167,18 @@
         assertThat(typeToString(backNavigationInfo.getType()))
                 .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
 
+        // reset drawing status to test if previous task is translucent activity
+        backNavigationInfo.onBackNavigationFinished(false);
+        mBackNavigationController.clearBackAnimations();
+        // simulate translucent
+        recordA.setOccludesParent(false);
+        backNavigationInfo = startBackNavigation();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
+
         // reset drawing status to test keyguard occludes
         topActivity.setOccludesParent(true);
+        recordA.setOccludesParent(true);
         backNavigationInfo.onBackNavigationFinished(false);
         mBackNavigationController.clearBackAnimations();
         makeWindowVisibleAndDrawn(topActivity.findMainWindow());
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 353ba01..89ae802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
 import static com.android.server.wm.DesktopModeLaunchParamsModifier.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
+import static com.android.server.wm.DesktopModeLaunchParamsModifier.ENFORCE_DEVICE_RESTRICTIONS_KEY;
 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;
@@ -29,18 +30,21 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.SystemProperties;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier.Result;
 import com.android.window.flags.Flags;
 
@@ -48,6 +52,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 /**
  * Tests for desktop mode task bounds.
@@ -71,8 +76,6 @@
     @Before
     public void setUp() throws Exception {
         mActivity = new ActivityBuilder(mAtm).build();
-        mTarget = spy(new DesktopModeLaunchParamsModifier(mContext));
-        doReturn(true).when(mTarget).isDesktopModeSupported(any());
         mCurrent = new LaunchParamsController.LaunchParams();
         mCurrent.reset();
         mResult = new LaunchParamsController.LaunchParams();
@@ -82,20 +85,26 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsContinueIfDesktopWindowingIsDisabled() {
+        setupDesktopModeLaunchParamsModifier();
+
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsContinueIfDesktopWindowingIsEnabledOnUnsupportedDevice() {
-        doReturn(false).when(mTarget).isDesktopModeSupported(any());
+        setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ false,
+                /*enforceDeviceRestrictions=*/ true);
+
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(null).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsDoneIfDesktopWindowingIsEnabledAndUnsupportedDeviceOverridden() {
-        doReturn(false).when(mTarget).enforceDeviceRestrictions();
+        setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
+                /*enforceDeviceRestrictions=*/ false);
+
         final Task task = new TaskBuilder(mSupervisor).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
     }
@@ -103,12 +112,16 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfTaskIsNull() {
+        setupDesktopModeLaunchParamsModifier();
+
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(null).calculate());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfNotBoundsPhase() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).build();
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).setPhase(
                 PHASE_DISPLAY).calculate());
@@ -117,6 +130,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfTaskNotUsingActivityTypeStandardOrUndefined() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_ASSISTANT).build();
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
@@ -125,6 +140,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsDoneIfTaskUsingActivityTypeStandard() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
@@ -133,6 +150,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsDoneIfTaskUsingActivityTypeUndefined() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_UNDEFINED).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
@@ -141,6 +160,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testReturnsSkipIfCurrentParamsHasBounds() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100);
@@ -150,6 +171,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testUsesDefaultBounds() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         final int displayHeight = 1600;
@@ -165,6 +188,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void testUsesDisplayAreaAndWindowingModeFromSource() {
+        setupDesktopModeLaunchParamsModifier();
+
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).build();
         TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class);
@@ -176,6 +201,24 @@
         assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
     }
 
+    private void setupDesktopModeLaunchParamsModifier() {
+        setupDesktopModeLaunchParamsModifier(/*isDesktopModeSupported=*/ true,
+                /*enforceDeviceRestrictions=*/ true);
+    }
+
+    private void setupDesktopModeLaunchParamsModifier(boolean isDesktopModeSupported,
+            boolean enforceDeviceRestrictions) {
+        Resources mockResources = Mockito.mock(Resources.class);
+        when(mockResources.getBoolean(eq(R.bool.config_isDesktopModeSupported)))
+                .thenReturn(isDesktopModeSupported);
+        doReturn(mockResources).when(mContext).getResources();
+
+        SystemProperties.set(ENFORCE_DEVICE_RESTRICTIONS_KEY,
+                String.valueOf(enforceDeviceRestrictions));
+
+        mTarget = new DesktopModeLaunchParamsModifier(mContext);
+    }
+
     private class CalculateRequestBuilder {
         private Task mTask;
         private int mPhase = PHASE_BOUNDS;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ac1dc08..8677738 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4193,11 +4193,49 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
+    public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() {
+        // Set up portrait close to square display.
+        setUpDisplaySizeWithApp(2200, 2280);
+        final DisplayContent display = mActivity.mDisplayContent;
+        display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape.
+        final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
+                "navbar");
+        final Binder owner = new Binder();
+        navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
+                new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
+                        .setInsetsSize(Insets.of(0, 0, 0, 150))
+        };
+        display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
+        assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
+        display.sendNewConfiguration();
+
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // Activity should not be letterboxed and should have portrait app bounds even though
+        // orientation is not respected with insets as insets have been decoupled.
+        final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
+        final Rect displayBounds = display.getBounds();
+        assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertNotNull(appBounds);
+        assertEquals(displayBounds.width(), appBounds.width());
+        assertEquals(displayBounds.height(), appBounds.height());
+    }
+
+    @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() {
         // Set up portrait close to square display
         setUpDisplaySizeWithApp(2200, 2280);
         final DisplayContent display = mActivity.mDisplayContent;
+        display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
                 "navbar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 5fe71a1..a78fc10 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -103,6 +103,7 @@
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
+import android.view.WindowRelayoutResult;
 import android.window.ActivityWindowInfo;
 import android.window.ClientWindowFrames;
 import android.window.InputTransferToken;
@@ -262,7 +263,9 @@
     }
 
     @Test
-    public void testRelayoutExitingWindow() {
+    public void testRelayoutExitingWindow_legacy() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
+
         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
         final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
         win.mWinAnimator.mSurfaceController = surfaceController;
@@ -320,6 +323,67 @@
     }
 
     @Test
+    public void testRelayoutExitingWindow() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
+
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
+        final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
+        win.mWinAnimator.mSurfaceController = surfaceController;
+        win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+        doReturn(true).when(surfaceController).hasSurface();
+        spyOn(win.mTransitionController);
+        doReturn(true).when(win.mTransitionController).isShellTransitionsEnabled();
+        doReturn(true).when(win.mTransitionController).inTransition(
+                eq(win.mActivityRecord));
+        win.mViewVisibility = View.VISIBLE;
+        win.mHasSurface = true;
+        win.mActivityRecord.mAppStopped = true;
+        mWm.mWindowMap.put(win.mClient.asBinder(), win);
+        spyOn(mWm.mWindowPlacerLocked);
+        // Skip unnecessary operations of relayout.
+        doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
+        final int w = 100;
+        final int h = 200;
+        final ClientWindowFrames outFrames = new ClientWindowFrames();
+        final MergedConfiguration outConfig = new MergedConfiguration();
+        final SurfaceControl outSurfaceControl = new SurfaceControl();
+        final InsetsState outInsetsState = new InsetsState();
+        final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
+        final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(outFrames,
+                outConfig, outSurfaceControl, outInsetsState, outControls);
+        mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
+                outRelayoutResult);
+        // The window is in transition, so its destruction is deferred.
+        assertTrue(win.mAnimatingExit);
+        assertFalse(win.mDestroying);
+        assertTrue(win.mTransitionController.mAnimatingExitWindows.contains(win));
+
+        win.mAnimatingExit = false;
+        win.mViewVisibility = View.VISIBLE;
+        win.mActivityRecord.setVisibleRequested(false);
+        win.mActivityRecord.setVisible(false);
+        mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
+                outRelayoutResult);
+        // Because the window is already invisible, it doesn't need to apply exiting animation
+        // and WMS#tryStartExitingAnimation() will destroy the surface directly.
+        assertFalse(win.mAnimatingExit);
+        assertFalse(win.mHasSurface);
+        assertNull(win.mWinAnimator.mSurfaceController);
+
+        // Invisible requested activity should not get the last config even if its view is visible.
+        mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
+                outRelayoutResult);
+        assertEquals(0, outConfig.getMergedConfiguration().densityDpi);
+        // Non activity window can still get the last config.
+        win.mActivityRecord = null;
+        win.fillClientWindowFramesAndConfiguration(outFrames, outConfig,
+                null /* outActivityWindowInfo*/, false /* useLatestConfig */,
+                true /* relayoutVisible */);
+        assertEquals(win.getConfiguration().densityDpi,
+                outConfig.getMergedConfiguration().densityDpi);
+    }
+
+    @Test
     public void testRelayout_firstLayout_dwpcHelperCalledWithCorrectFlags() {
         // When doing the first layout, the initial flags should be reported as changed to
         // keepActivityOnWindowFlagsChanged.
@@ -417,10 +481,15 @@
             win.mRelayoutSeq = 1;
             seq = 2;
         }
-        mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
-                0, new ClientWindowFrames(), new MergedConfiguration(),
-                new SurfaceControl(), new InsetsState(), new InsetsSourceControl.Array(),
-                new Bundle());
+        if (Flags.windowSessionRelayoutInfo()) {
+            mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
+                    0, new WindowRelayoutResult());
+        } else {
+            mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
+                    0, new ClientWindowFrames(), new MergedConfiguration(),
+                    new SurfaceControl(), new InsetsState(), new InsetsSourceControl.Array(),
+                    new Bundle());
+        }
 
         ArgumentCaptor<Integer> changedFlags = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Integer> changedPrivateFlags = ArgumentCaptor.forClass(Integer.class);
@@ -1245,8 +1314,9 @@
     }
 
     @Test
-    public void testRelayout_appWindowSendActivityWindowInfo() {
+    public void testRelayout_appWindowSendActivityWindowInfo_legacy() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+        mSetFlagsRule.disableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
 
         // Skip unnecessary operations of relayout.
         spyOn(mWm.mWindowPlacerLocked);
@@ -1305,6 +1375,65 @@
     }
 
     @Test
+    public void testRelayout_appWindowSendActivityWindowInfo() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+        mSetFlagsRule.enableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
+
+        // Skip unnecessary operations of relayout.
+        spyOn(mWm.mWindowPlacerLocked);
+        doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
+
+        final Task task = createTask(mDisplayContent);
+        final WindowState win = createAppWindow(task, ACTIVITY_TYPE_STANDARD, "appWindow");
+        mWm.mWindowMap.put(win.mClient.asBinder(), win);
+
+        final int w = 100;
+        final int h = 200;
+        final ClientWindowFrames outFrames = new ClientWindowFrames();
+        final MergedConfiguration outConfig = new MergedConfiguration();
+        final SurfaceControl outSurfaceControl = new SurfaceControl();
+        final InsetsState outInsetsState = new InsetsState();
+        final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
+        final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(outFrames,
+                outConfig, outSurfaceControl, outInsetsState, outControls);
+
+        final ActivityRecord activity = win.mActivityRecord;
+        final ActivityWindowInfo expectedInfo = new ActivityWindowInfo();
+        expectedInfo.set(true, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 500, 2000));
+        doReturn(expectedInfo).when(activity).getActivityWindowInfo();
+        activity.setVisibleRequested(false);
+        activity.setVisible(false);
+
+        mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
+                outRelayoutResult);
+
+        // No latest reported value, so return empty when activity is invisible
+        final ActivityWindowInfo activityWindowInfo = outRelayoutResult.activityWindowInfo;
+        assertEquals(new ActivityWindowInfo(), activityWindowInfo);
+
+        activity.setVisibleRequested(true);
+        activity.setVisible(true);
+
+        mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
+                outRelayoutResult);
+
+        // Report the latest when activity is visible.
+        final ActivityWindowInfo activityWindowInfo2 = outRelayoutResult.activityWindowInfo;
+        assertEquals(expectedInfo, activityWindowInfo2);
+
+        expectedInfo.set(false, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 1000, 2000));
+        activity.setVisibleRequested(false);
+        activity.setVisible(false);
+
+        mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
+                outRelayoutResult);
+
+        // Report the last reported value when activity is invisible.
+        final ActivityWindowInfo activityWindowInfo3 = outRelayoutResult.activityWindowInfo;
+        assertEquals(activityWindowInfo2, activityWindowInfo3);
+    }
+
+    @Test
     public void testAddOverlayWindowToUnassignedDisplay_notAllowed_ForVisibleBackgroundUsers() {
         doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled());
         int uid = 100000; // uid for non-system user
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index e20f822..fbbb9a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -104,6 +104,7 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowRelayoutResult;
 import android.view.inputmethod.ImeTracker;
 import android.window.ClientWindowFrames;
 import android.window.ITaskFragmentOrganizer;
@@ -113,6 +114,7 @@
 
 import com.android.server.testutils.StubTransaction;
 import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.Test;
@@ -1350,7 +1352,9 @@
 
     @SetupWindows(addWindows = {W_INPUT_METHOD})
     @Test
-    public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() {
+    public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged_legacy() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
+
         final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
         mWm.mImeTargetChangeListener = listener;
 
@@ -1404,6 +1408,65 @@
         assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
     }
 
+    @SetupWindows(addWindows = {W_INPUT_METHOD})
+    @Test
+    public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
+
+        final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
+        mWm.mImeTargetChangeListener = listener;
+
+        // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible.
+        final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+                mDisplayContent);
+        final IWindow client = new TestIWindow();
+        final Session session = getTestSession();
+        final ClientWindowFrames outFrames = new ClientWindowFrames();
+        final MergedConfiguration outConfig = new MergedConfiguration();
+        final SurfaceControl outSurfaceControl = new SurfaceControl();
+        final InsetsState outInsetsState = new InsetsState();
+        final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
+        final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(outFrames,
+                outConfig, outSurfaceControl, outInsetsState, outControls);
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                TYPE_APPLICATION_OVERLAY);
+        params.setTitle("imeLayeringTargetOverlay");
+        params.token = windowToken.token;
+        params.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
+
+        mWm.addWindow(session, client, params, View.VISIBLE, DEFAULT_DISPLAY,
+                0 /* userUd */, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
+                new InsetsSourceControl.Array(), new Rect(), new float[1]);
+        mWm.relayoutWindow(session, client, params, 100, 200, View.VISIBLE, 0, 0, 0,
+                outRelayoutResult);
+        waitHandlerIdle(mWm.mH);
+
+        final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow(
+                w -> w.mClient.asBinder() == client.asBinder());
+        assertThat(imeLayeringTargetOverlay.isVisible()).isTrue();
+        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+        assertThat(listener.mIsRemoved).isFalse();
+        assertThat(listener.mIsVisibleForImeTargetOverlay).isTrue();
+
+        // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible.
+        mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0,
+                outRelayoutResult);
+        waitHandlerIdle(mWm.mH);
+
+        assertThat(imeLayeringTargetOverlay.isVisible()).isFalse();
+        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+        assertThat(listener.mIsRemoved).isFalse();
+        assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
+
+        // Scenario 3: test removeWindow to remove the Ime layering target overlay window.
+        mWm.removeClientToken(session, client.asBinder());
+        waitHandlerIdle(mWm.mH);
+
+        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+        assertThat(listener.mIsRemoved).isTrue();
+        assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
+    }
+
     @Test
     @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
     public void testIsSecureLocked_sensitiveContentProtectionManagerEnabled() {
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index 988f76f..38442db 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -10,6 +10,8 @@
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
         <!-- keeps the screen on during tests -->
         <option name="screen-always-on" value="on"/>
+        <!-- enable AOD -->
+        <option name="set-secure-setting" key="doze_always_on" value="1" />
         <!-- prevents the phone from restarting -->
         <option name="force-skip-system-props" value="true"/>
         <!-- set WM tracing verbose level to all -->
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
new file mode 100644
index 0000000..31506b5
--- /dev/null
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.Rotation
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing on lock and opening on screen unlock. To run this test: `atest
+ * FlickerTests:CloseImeWindowToHomeTest`
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ShowImeOnUnlockScreenTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+    private val testApp = ImeAppHelper(instrumentation)
+    private val imeOrSnapshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SNAPSHOT)
+
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit = {
+        setup {
+            tapl.expectedRotationCheckEnabled = false
+            testApp.launchViaIntent(wmHelper)
+            testApp.openIME(wmHelper)
+        }
+        transitions {
+            device.sleep()
+            wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
+            UnlockScreenRule.unlockScreen(device)
+            wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
+        }
+        teardown { testApp.exit(wmHelper) }
+    }
+
+    @Presubmit
+    @Test
+    fun imeAndAppAnimateTogetherWhenLockingAndUnlocking() {
+        flicker.assertLayers {
+            this.isVisible(testApp)
+                .isVisible(imeOrSnapshot)
+                .then()
+                .isInvisible(testApp)
+                .isInvisible(imeOrSnapshot)
+                .then()
+                .isVisible(testApp)
+                .isVisible(imeOrSnapshot)
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Not applicable to this CUJ. Display turns off during transition")
+    override fun navBarWindowIsAlwaysVisible() {}
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Not applicable to this CUJ. Display turns off during transition")
+    override fun statusBarWindowIsAlwaysVisible() {}
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Not applicable to this CUJ. Display turns off during transition")
+    override fun taskBarWindowIsAlwaysVisible() {}
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() =
+            LegacyFlickerTestFactory.nonRotationTests(
+                supportedRotations = listOf(Rotation.ROTATION_0)
+            )
+    }
+}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 7c5dcf8..e8be33c 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -51,6 +51,7 @@
             "android.view.CutoutSpecificationTest",
             "android.view.DisplayCutoutTest",
             "android.view.DisplayShapeTest",
+            "android.view.ImeBackAnimationControllerTest",
             "android.view.InsetsAnimationControlImplTest",
             "android.view.InsetsControllerTest",
             "android.view.InsetsFlagsTest",
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 " +