Merge "Adding animation to edit widget button" into main
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 74382a6..3ab0934 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -270,6 +270,7 @@
     ],
     jni_libs: [
         "libandroid_runtime",
+        "libravenwood_runtime",
     ],
 }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0372b7b..a938bee 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3989,15 +3989,15 @@
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void addVirtualStylusIdForTestSession();
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void finishTrackingPendingImeVisibilityRequests();
     method public int getDisplayId();
-    method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
-    method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
     method public boolean hasActiveInputConnection(@Nullable android.view.View);
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest();
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
-    method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
     field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
   }
diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java
index f0bc3e2..37e51f8 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -104,7 +104,7 @@
      * Sets the current grammatical gender for all privileged applications. The value will be
      * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)}
      *
-     * @param grammaticalGender the terms of address the user preferred in system.
+     * @param grammaticalGender the grammatical gender set by the user for the system.
      *
      * @see Configuration#getGrammaticalGender
      * @hide
@@ -123,12 +123,12 @@
     }
 
     /**
-     * Get the current grammatical gender of privileged application from the encrypted file.
+     * Allows privileged preloaded applications to get the system grammatical gender when set.
      *
-     * @return the value of system grammatical gender only if the calling app has the permission,
-     * otherwise throwing an exception.
+     * @return The value of system grammatical gender only if the calling app has the
+     * permission, otherwise throwing an exception.
      *
-     * @throws SecurityException if the caller does not have the required permission.
+     * @throws SecurityException If the caller does not have the required permission.
      *
      * @see Configuration#getGrammaticalGender
      */
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 8171723..9437c74 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -300,6 +300,16 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     static final long ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN = 330902016;
 
+    /**
+     * The corresponding vendor API for Android V
+     *
+     * <p>Starting with Android V, the vendor API format has switched to YYYYMM.
+     *
+     * @see <a href="https://preview.source.android.com/docs/core/architecture/api-flags">Vendor API
+     *     level</a>
+     */
+    private static final int VENDOR_API_FOR_ANDROID_V = 202404;
+
     // Service registry information.
     // This information is never changed once static initialization has completed.
     private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES =
@@ -465,9 +475,10 @@
                 new CachedServiceFetcher<VcnManager>() {
             @Override
             public VcnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                if (shouldCheckTelephonyFeatures()
-                    && !ctx.getPackageManager().hasSystemFeature(
-                            PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)) {
+                final String telephonyFeatureToCheck = getVcnFeatureDependency();
+
+                if (telephonyFeatureToCheck != null
+                    && !ctx.getPackageManager().hasSystemFeature(telephonyFeatureToCheck)) {
                     return null;
                 }
 
@@ -1768,16 +1779,24 @@
     // partition SDK level, not application's target SDK version (which BTW we
     // also check through Compatibility framework a few lines below).
     @SuppressWarnings("AndroidFrameworkCompatChange")
-    private static boolean shouldCheckTelephonyFeatures() {
+    @Nullable
+    private static String getVcnFeatureDependency() {
+        // Check SDK version of the client app. Apps targeting pre-V SDK might
+        // have not checked for existence of these features.
+        if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN)) {
+            return null;
+        }
+
         // Check SDK version of the vendor partition. Pre-V devices might have
         // incorrectly under-declared telephony features.
         final int vendorApiLevel = SystemProperties.getInt(
                 "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
-        if (vendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) return false;
+        if (vendorApiLevel < VENDOR_API_FOR_ANDROID_V) {
+            return PackageManager.FEATURE_TELEPHONY;
+        } else {
+            return PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION;
+        }
 
-        // Check SDK version of the client app. Apps targeting pre-V SDK might
-        // have not checked for existence of these features.
-        return Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN);
     }
 
     /**
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index e5f2976..23b2ea4 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
 import android.annotation.SystemService;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -40,6 +41,15 @@
 @SystemService(Context.TRUST_SERVICE)
 public class TrustManager {
 
+    /**
+     * Intent action used to identify services that can serve as significant providers.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER =
+            "com.android.trust.provider.SignificantPlaceProvider.BIND";
+
     private static final int MSG_TRUST_CHANGED = 1;
     private static final int MSG_TRUST_MANAGED_CHANGED = 2;
     private static final int MSG_TRUST_ERROR = 3;
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 6952e5d..f7a22f5 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -3,3 +3,4 @@
 include /services/core/java/com/android/server/display/OWNERS
 
 quxiangfang@google.com
+donpaul@google.com
\ No newline at end of file
diff --git a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
new file mode 100644
index 0000000..e02169e
--- /dev/null
+++ b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
@@ -0,0 +1,10 @@
+package android.hardware.location;
+
+import android.hardware.location.ISignificantPlaceProviderManager;
+
+/**
+ * @hide
+ */
+oneway interface ISignificantPlaceProvider {
+    void setSignificantPlaceProviderManager(in ISignificantPlaceProviderManager manager);
+}
diff --git a/core/java/android/hardware/location/ISignificantPlaceProviderManager.aidl b/core/java/android/hardware/location/ISignificantPlaceProviderManager.aidl
new file mode 100644
index 0000000..76eefe7
--- /dev/null
+++ b/core/java/android/hardware/location/ISignificantPlaceProviderManager.aidl
@@ -0,0 +1,8 @@
+package android.hardware.location;
+
+/**
+ * @hide
+ */
+interface ISignificantPlaceProviderManager {
+    void setInSignificantPlace(in boolean inSignificantPlace);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 65d9b3a..cb5a885 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -62,7 +62,6 @@
 import android.view.InputDevice;
 import android.view.IInputFilter;
 import android.view.AppTransitionAnimationSpec;
-import android.view.TaskTransitionSpec;
 import android.view.WindowContentFrameStats;
 import android.view.WindowManager;
 import android.view.SurfaceControl;
@@ -962,19 +961,6 @@
     void setTaskSnapshotEnabled(boolean enabled);
 
     /**
-     * Customized the task transition animation with a task transition spec.
-     *
-     * @param spec the spec that will be used to customize the task animations
-     */
-    void setTaskTransitionSpec(in TaskTransitionSpec spec);
-
-    /**
-     * Clears any task transition spec that has been previously set and
-     * reverts to using the default task transition with no spec changes.
-     */
-    void clearTaskTransitionSpec();
-
-    /**
      * Registers the frame rate per second count callback for one given task ID.
      * Each callback can only register for receiving FPS callback for one task id until unregister
      * is called. If there's no task associated with the given task id,
diff --git a/core/java/android/view/TaskTransitionSpec.aidl b/core/java/android/view/TaskTransitionSpec.aidl
deleted file mode 100644
index 08af15c..0000000
--- a/core/java/android/view/TaskTransitionSpec.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-** Copyright 2021, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.view;
-
-/** @hide */
-parcelable TaskTransitionSpec;
diff --git a/core/java/android/view/TaskTransitionSpec.java b/core/java/android/view/TaskTransitionSpec.java
deleted file mode 100644
index 9a2d3ba..0000000
--- a/core/java/android/view/TaskTransitionSpec.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Holds information about how to execute task transition animations.
- *
- * This class is intended to be used with IWindowManager.setTaskTransitionSpec methods when
- * we want more customization over the way default task transitions are executed.
- *
- * @hide
- */
-public class TaskTransitionSpec implements Parcelable {
-    /**
-     * The background color to use during task animations (override the default background color)
-     */
-    public final int backgroundColor;
-
-    public TaskTransitionSpec(int backgroundColor) {
-        this.backgroundColor = backgroundColor;
-    }
-
-    public TaskTransitionSpec(Parcel in) {
-        this.backgroundColor = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(backgroundColor);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<TaskTransitionSpec>
-            CREATOR = new Parcelable.Creator<TaskTransitionSpec>() {
-                public TaskTransitionSpec createFromParcel(Parcel in) {
-                    return new TaskTransitionSpec(in);
-                }
-
-                public TaskTransitionSpec[] newArray(int size) {
-                    return new TaskTransitionSpec[size];
-                }
-            };
-}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a844370..fe005d0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -33944,8 +33944,9 @@
     protected int calculateFrameRateCategory() {
         int category;
         switch (getViewRootImpl().intermittentUpdateState()) {
-            case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT ->
-                    category = FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
+            case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> category =
+                    (sToolkitFrameRateBySizeReadOnlyFlagValue ? FRAME_RATE_CATEGORY_LOW
+                            : FRAME_RATE_CATEGORY_NORMAL) | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
             case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT ->
                     category = mSizeBasedFrameRateCategoryAndReason;
             default -> category = mLastFrameRateCategory;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 11ad86c..ab529e6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2654,9 +2654,10 @@
             ViewRootImpl viewRootImpl = getViewRootImpl();
             if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
-                final boolean isDispatchingBack = (viewRootImpl != null
-                        && viewRootImpl.getOnBackInvokedDispatcher().isDispatching());
-                if (!disallowIntercept || isDispatchingBack) { // Allow back to intercept touch
+                final boolean isBackGestureInProgress = (viewRootImpl != null
+                        && viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress());
+                if (!disallowIntercept || isBackGestureInProgress) {
+                    // Allow back to intercept touch
                     intercepted = onInterceptTouchEvent(ev);
                     ev.setAction(action); // restore action in case it was changed
                 } else {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f51d909..63121bd8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7228,7 +7228,7 @@
         private int doOnBackKeyEvent(KeyEvent keyEvent) {
             WindowOnBackInvokedDispatcher dispatcher = getOnBackInvokedDispatcher();
             OnBackInvokedCallback topCallback = dispatcher.getTopCallback();
-            if (dispatcher.isDispatching()) {
+            if (dispatcher.isBackGestureInProgress()) {
                 return FINISH_NOT_HANDLED;
             }
             if (topCallback instanceof OnBackAnimationCallback) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 8174da6..1cdcd20 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1671,7 +1671,6 @@
     @NonNull
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
     @TestApi
-    @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
     @SuppressLint("UserHandle")
     public boolean isStylusHandwritingAvailableAsUser(@NonNull UserHandle user) {
         final Context fallbackContext = ActivityThread.currentApplication();
@@ -1816,7 +1815,6 @@
     @NonNull
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
     @TestApi
-    @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
     @SuppressLint("UserHandle")
     public List<InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull UserHandle user) {
         return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(user.getIdentifier());
@@ -1858,7 +1856,6 @@
     @NonNull
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
     @TestApi
-    @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
     @SuppressLint("UserHandle")
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
             @NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes,
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index d79903b..fa9458d 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -106,3 +106,13 @@
     }
 }
 
+flag {
+  name: "defer_show_soft_input_until_session_creation"
+  namespace: "input_method"
+  description: "Defers showSoftInput until the IME session has been created."
+  bug: "337766845"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index cf0c015..e351d6b 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -114,7 +114,7 @@
 
     /** Updates the dispatcher state on a new {@link MotionEvent}. */
     public void onMotionEvent(MotionEvent ev) {
-        if (!isDispatching() || ev == null || ev.getAction() != MotionEvent.ACTION_MOVE) {
+        if (!isBackGestureInProgress() || ev == null || ev.getAction() != MotionEvent.ACTION_MOVE) {
             return;
         }
         mTouchTracker.update(ev.getX(), ev.getY(), Float.NaN, Float.NaN);
@@ -246,9 +246,9 @@
     }
 
     /**
-     * Indicates if the dispatcher is actively dispatching to a callback.
+     * Indicates if a user gesture is currently in progress.
      */
-    public boolean isDispatching() {
+    public boolean isBackGestureInProgress() {
         synchronized (mLock) {
             return mTouchTracker.isActive() || mImeDispatchingActive;
         }
@@ -475,12 +475,17 @@
         @Override
         public void onBackStarted(BackMotionEvent backEvent) {
             mHandler.post(() -> {
+                final OnBackAnimationCallback callback = getBackAnimationCallback();
+
+                // reset progress animator before dispatching onBackStarted to callback. This
+                // ensures that onBackCancelled (of a previous gesture) is always dispatched
+                // before onBackStarted
+                if (callback != null) mProgressAnimator.reset();
                 mTouchTracker.setState(BackTouchTracker.TouchTrackerState.ACTIVE);
                 mTouchTracker.setShouldUpdateStartLocation(true);
                 mTouchTracker.setGestureStartLocation(
                         backEvent.getTouchX(), backEvent.getTouchY(), backEvent.getSwipeEdge());
 
-                final OnBackAnimationCallback callback = getBackAnimationCallback();
                 if (callback != null) {
                     callback.onBackStarted(new BackEvent(
                             backEvent.getTouchX(),
@@ -499,14 +504,9 @@
         public void onBackCancelled() {
             mHandler.post(() -> {
                 final OnBackAnimationCallback callback = getBackAnimationCallback();
-                if (callback == null) {
-                    mTouchTracker.reset();
-                    return;
-                }
-                mProgressAnimator.onBackCancelled(() -> {
-                    mTouchTracker.reset();
-                    callback.onBackCancelled();
-                });
+                mTouchTracker.reset();
+                if (callback == null) return;
+                mProgressAnimator.onBackCancelled(callback::onBackCancelled);
             });
         }
 
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 55927cc..e8b4f0b 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -64,3 +64,10 @@
     description: "Hides the App Handle when in fullscreen immersive mode"
     bug: "336368019"
 }
+
+flag {
+    name: "enable_desktop_windowing_quick_switch"
+    namespace: "lse_desktop_experience"
+    description: "Enables quick switch for desktop mode"
+    bug: "338066529"
+}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index f4315e3..74c2325 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -523,8 +523,8 @@
         ViewRootImpl viewRootImpl = getViewRootImpl();
         if (viewRootImpl != null) {
             viewRootImpl.getOnBackInvokedDispatcher().onMotionEvent(event);
-            // Intercept touch if back dispatching is active.
-            if (viewRootImpl.getOnBackInvokedDispatcher().isDispatching()) {
+            // Intercept touch if back gesture is in progress.
+            if (viewRootImpl.getOnBackInvokedDispatcher().isBackGestureInProgress()) {
                 return true;
             }
         }
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 874cc49..ee33eb4 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -99,7 +99,7 @@
     private final Map<LogLevel, Integer> mDefaultLogLevelCounts = new ArrayMap<>();
     private final Map<IProtoLogGroup, Map<LogLevel, Integer>> mLogLevelCounts = new ArrayMap<>();
 
-    private final ExecutorService mBackgroundLoggingService = Executors.newCachedThreadPool();
+    private final ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor();
 
     public PerfettoProtoLogImpl(String viewerConfigFilePath,
             TreeMap<String, IProtoLogGroup> logGroups, Runnable cacheUpdater) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3ed9f49..12d62cc 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1828,10 +1828,10 @@
   std::string source = "/dev/__properties__/appcompat_override";
   std::string target = "/dev/__properties__";
   if (access(source.c_str(), F_OK) != 0) {
-    fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno)));
+      return;
   }
   if (access(target.c_str(), F_OK) != 0) {
-    fail_fn(CREATE_ERROR("Error accessing %s: %s", target.c_str(), strerror(errno)));
+      return;
   }
   BindMount(source, target, fail_fn);
   // Reload the system properties file, to ensure new values are read into memory
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 8ae2a9b..6ce4b9d 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -21,16 +21,102 @@
     android:viewportHeight="24">
   <group>
     <clip-path
-        android:pathData="M12,20.923C10.764,20.923 9.946,20.065 9.131,18.653C8.316,17.241 4.291,10.269 3.476,8.857C2.66,7.445 2.326,6.309 2.944,5.238C3.563,4.167 4.714,3.888 6.344,3.888C7.975,3.888 16.025,3.888 17.656,3.888C19.286,3.888 20.437,4.167 21.056,5.238C21.674,6.309 21.34,7.445 20.524,8.857C19.709,10.269 15.684,17.241 14.869,18.653C14.054,20.065 13.236,20.923 12,20.923H12Z"/>
+        android:pathData="
+            M12.6495 17.375
+            C12.3608 17.875 11.6392 17.875 11.3505 17.375
+            L3.98927 4.625
+            C3.70059 4.125 4.06143 3.5 4.63878 3.5
+            L19.3612 3.5
+            C19.9386 3.5 20.2994 4.125 20.0107 4.625
+            L12.6495 17.375
+            Z
+        "/>
     <path
-        android:pathData="M5,14.978h14v9.8h-14z"
+        android:pathData="M5,12 h14v9.8h-14z"
         android:fillColor="#ffffff"/>
     <path
-        android:pathData="M18.722,15.576C18.717,15.548 18.713,15.521 18.708,15.493C18.68,15.324 18.646,15.156 18.605,14.991C18.534,14.701 18.445,14.42 18.339,14.146C18.249,13.915 18.146,13.69 18.033,13.472C17.886,13.192 17.722,12.923 17.539,12.667C17.316,12.354 17.067,12.06 16.795,11.789C16.68,11.676 16.562,11.566 16.44,11.461C16.175,11.233 15.893,11.025 15.595,10.839C15.598,10.834 15.6,10.83 15.602,10.825C15.739,10.59 15.875,10.355 16.012,10.119C16.145,9.889 16.279,9.659 16.412,9.429C16.508,9.264 16.604,9.098 16.699,8.933C16.722,8.894 16.74,8.854 16.753,8.812C16.791,8.696 16.792,8.575 16.762,8.462C16.754,8.434 16.745,8.406 16.734,8.38C16.723,8.353 16.71,8.327 16.695,8.302C16.644,8.216 16.571,8.142 16.479,8.087C16.399,8.039 16.308,8.011 16.215,8.002C16.176,7.999 16.137,7.999 16.098,8.003C16.066,8.007 16.034,8.013 16.002,8.021C15.889,8.051 15.785,8.113 15.703,8.202C15.674,8.235 15.647,8.27 15.624,8.309C15.529,8.475 15.433,8.64 15.337,8.805L14.937,9.495C14.801,9.731 14.664,9.966 14.528,10.202C14.513,10.227 14.498,10.253 14.483,10.279C14.462,10.271 14.442,10.263 14.421,10.255C13.669,9.968 12.853,9.811 12,9.811C11.977,9.811 11.954,9.811 11.931,9.811C11.172,9.819 10.444,9.951 9.764,10.188C9.686,10.215 9.608,10.244 9.531,10.274C9.517,10.25 9.503,10.226 9.489,10.202C9.353,9.966 9.216,9.731 9.08,9.495C8.946,9.265 8.813,9.035 8.679,8.805C8.584,8.64 8.488,8.475 8.392,8.31C8.37,8.271 8.343,8.235 8.314,8.203C8.232,8.113 8.127,8.051 8.014,8.021C7.983,8.013 7.951,8.007 7.919,8.004C7.88,8 7.841,7.999 7.802,8.003C7.709,8.011 7.618,8.039 7.537,8.088C7.446,8.142 7.373,8.217 7.322,8.302C7.307,8.327 7.294,8.353 7.283,8.38C7.271,8.407 7.262,8.434 7.255,8.462C7.225,8.575 7.226,8.697 7.264,8.812C7.277,8.854 7.295,8.894 7.318,8.934C7.413,9.099 7.509,9.264 7.605,9.429C7.738,9.659 7.872,9.889 8.005,10.119C8.141,10.355 8.278,10.59 8.414,10.826C8.415,10.828 8.417,10.83 8.418,10.832C8.143,11.003 7.881,11.192 7.634,11.4C7.486,11.524 7.343,11.654 7.207,11.79C6.935,12.061 6.685,12.354 6.462,12.668C6.279,12.923 6.114,13.192 5.968,13.472C5.855,13.691 5.752,13.915 5.662,14.147C5.556,14.42 5.467,14.702 5.396,14.991C5.355,15.157 5.321,15.324 5.293,15.494C5.288,15.521 5.284,15.549 5.279,15.576C5.264,15.675 5.251,15.774 5.241,15.874L18.759,15.874C18.749,15.774 18.736,15.675 18.72,15.576L18.722,15.576Z"
+        android:pathData="
+            M15.97 10.48
+            C15.97 10.46 15.97 10.45 15.96 10.43
+            C15.95 10.33 15.93 10.23 15.90 10.13
+            C15.86 9.96 15.81 9.79 15.75 9.63
+            C15.69 9.50 15.63 9.36 15.57 9.23
+            C15.48 9.07 15.38 8.91 15.27 8.76
+            C15.14 8.57 14.99 8.40 14.83 8.24
+            C14.76 8.17 14.69 8.11 14.62 8.04
+            C14.47 7.91 14.30 7.78 14.12 7.67
+            C14.12 7.67 14.13 7.67 14.13 7.67
+            C14.21 7.53 14.29 7.39 14.37 7.25
+            C14.45 7.11 14.53 6.98 14.61 6.84
+            C14.66 6.74 14.72 6.64 14.78 6.55
+            C14.79 6.52 14.80 6.50 14.81 6.48
+            C14.83 6.41 14.83 6.33 14.81 6.27
+            C14.81 6.25 14.80 6.24 14.80 6.22
+            C14.79 6.20 14.78 6.19 14.77 6.17
+            C14.74 6.12 14.70 6.08 14.65 6.05
+            C14.60 6.02 14.54 6 14.49 6
+            C14.47 5.99 14.44 5.99 14.42 6
+            C14.40 6 14.38 6 14.36 6.01
+            C14.30 6.02 14.23 6.06 14.19 6.11
+            C14.17 6.13 14.15 6.15 14.14 6.18
+            C14.08 6.28 14.03 6.37 13.97 6.47
+            L13.73 6.88
+            C13.65 7.02 13.57 7.16 13.49 7.30
+            C13.48 7.31 13.47 7.33 13.46 7.34
+            C13.45 7.34 13.44 7.33 13.43 7.33
+            C12.98 7.16 12.50 7.07 12 7.07
+            C11.98 7.07 11.97 7.07 11.95 7.07
+            C11.51 7.07 11.07 7.15 10.67 7.29
+            C10.63 7.31 10.58 7.32 10.53 7.34
+            C10.53 7.33 10.52 7.31 10.51 7.30
+            C10.43 7.16 10.35 7.02 10.27 6.88
+            C10.19 6.74 10.11 6.61 10.03 6.47
+            C9.97 6.37 9.92 6.28 9.86 6.18
+            C9.85 6.15 9.83 6.13 9.81 6.11
+            C9.77 6.06 9.70 6.03 9.64 6.01
+            C9.62 6 9.60 6 9.58 6
+            C9.56 5.99 9.53 5.99 9.51 6
+            C9.46 6 9.40 6.02 9.35 6.05
+            C9.30 6.08 9.26 6.12 9.23 6.17
+            C9.22 6.19 9.21 6.20 9.20 6.22
+            C9.20 6.24 9.19 6.25 9.19 6.27
+            C9.17 6.34 9.17 6.41 9.19 6.48
+            C9.20 6.50 9.21 6.52 9.22 6.55
+            C9.28 6.65 9.34 6.74 9.39 6.84
+            C9.47 6.98 9.55 7.11 9.63 7.25
+            C9.71 7.39 9.79 7.53 9.87 7.67
+            C9.87 7.67 9.87 7.67 9.88 7.67
+            C9.71 7.77 9.56 7.88 9.41 8.01
+            C9.32 8.08 9.24 8.16 9.16 8.24
+            C9 8.40 8.85 8.57 8.72 8.76
+            C8.61 8.91 8.51 9.07 8.43 9.23
+            C8.36 9.36 8.30 9.50 8.24 9.63
+            C8.18 9.79 8.13 9.96 8.09 10.13
+            C8.06 10.23 8.04 10.33 8.03 10.43
+            C8.02 10.45 8.02 10.46 8.02 10.48
+            C8.01 10.54 8 10.60 8 10.65
+            L16 10.65
+            C15.99 10.60 15.98 10.54 15.97 10.48
+            L15.97 10.48
+            Z
+        "
         android:fillColor="#ffffff"/>
   </group>
   <path
-      android:pathData="M12,20.923C10.764,20.923 9.946,20.065 9.131,18.653C8.316,17.241 4.291,10.269 3.476,8.857C2.66,7.445 2.326,6.309 2.944,5.238C3.563,4.167 4.714,3.888 6.344,3.888C7.975,3.888 16.025,3.888 17.656,3.888C19.286,3.888 20.437,4.167 21.056,5.238C21.674,6.309 21.34,7.445 20.524,8.857C19.709,10.269 15.684,17.241 14.869,18.653C14.054,20.065 13.236,20.923 12,20.923H12Z"
+      android:pathData="
+          M12,20.923
+          C10.764,20.923 9.946,20.065 9.131,18.653
+          C8.316,17.241 4.291,10.269 3.476,8.857
+          C2.66,7.445 2.326,6.309 2.944,5.238
+          C3.563,4.167 4.714,3.888 6.344,3.888
+          C7.975,3.888 16.025,3.888 17.656,3.888
+          C19.286,3.888 20.437,4.167 21.056,5.238
+          C21.674,6.309 21.34,7.445 20.524,8.857
+          C19.709,10.269 15.684,17.241 14.869,18.653
+          C14.054,20.065 13.236,20.923 12,20.923
+          H12
+          Z
+      "
       android:strokeWidth="2"
       android:fillColor="#00000000"
       android:strokeColor="#ffffff"/>
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 32aec1a..8641373 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -488,16 +488,19 @@
         waitForFrameRateCategoryToSettle();
         for (int i = 0; i < 5; i++) {
             int expectedCategory;
-            if (i < 4) {
+            if (i < 2) {
                 // not intermittent yet.
                 // It takes 2 frames of intermittency before Views vote as intermittent.
-                // It takes 4 more frames for the category to drop to the next category.
                 expectedCategory =
                         toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL
                                 : FRAME_RATE_CATEGORY_HIGH;
             } else {
                 // intermittent
-                expectedCategory = FRAME_RATE_CATEGORY_NORMAL;
+                // Even though this is not a small View, step 3 is triggered by this flag, which
+                // brings intermittent to LOW
+                expectedCategory = toolkitFrameRateBySizeReadOnly()
+                        ? FRAME_RATE_CATEGORY_LOW
+                        : FRAME_RATE_CATEGORY_NORMAL;
             }
             mActivityRule.runOnUiThread(() -> {
                 mMovingView.invalidate();
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 7c098f2..a7f8176 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1174,9 +1174,16 @@
 
         // Infrequent update
         Thread.sleep(delay);
+
+        // Even though this is not a small View, step 3 is triggered by this flag, which
+        // brings intermittent to LOW
+        int intermittentExpected = toolkitFrameRateBySizeReadOnly()
+                ? FRAME_RATE_CATEGORY_LOW
+                : FRAME_RATE_CATEGORY_NORMAL;
+
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
-            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+            runAfterDraw(() -> assertEquals(intermittentExpected,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
         waitForAfterDraw();
@@ -1184,7 +1191,7 @@
         // When the View vote, it's still considered as intermittent update state
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
-            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
+            runAfterDraw(() -> assertEquals(intermittentExpected,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
         waitForAfterDraw();
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 1a8e75a..d54b862 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -51,6 +51,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -372,6 +373,34 @@
     }
 
     @Test
+    public void onBackCancelled_calledBeforeOnBackStartedOfNewGesture() throws RemoteException {
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+        OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
+
+        callbackInfo.getCallback().onBackStarted(mBackEvent);
+
+        waitForIdle();
+        verify(mCallback1).onBackStarted(any(BackEvent.class));
+        clearInvocations(mCallback1);
+
+        callbackInfo.getCallback().onBackCancelled();
+
+        waitForIdle();
+        // verify onBackCancelled not yet called (since BackProgressAnimator animates
+        // progress to 0 first)
+        verify(mCallback1, never()).onBackCancelled();
+
+        // simulate start of new gesture while cancel animation is still running
+        callbackInfo.getCallback().onBackStarted(mBackEvent);
+        waitForIdle();
+
+        // verify that onBackCancelled is called before onBackStarted
+        InOrder orderVerifier = Mockito.inOrder(mCallback1);
+        orderVerifier.verify(mCallback1).onBackCancelled();
+        orderVerifier.verify(mCallback1).onBackStarted(any(BackEvent.class));
+    }
+
+    @Test
     public void onDetachFromWindow_cancelCallbackAndIgnoreOnBackInvoked() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
 
@@ -399,11 +428,11 @@
 
         callbackInfo.getCallback().onBackStarted(mBackEvent);
         waitForIdle();
-        assertTrue(mDispatcher.isDispatching());
+        assertTrue(mDispatcher.isBackGestureInProgress());
 
         callbackInfo.getCallback().onBackInvoked();
         waitForIdle();
-        assertFalse(mDispatcher.isDispatching());
+        assertFalse(mDispatcher.isBackGestureInProgress());
     }
 
     @Test
@@ -418,7 +447,7 @@
 
         callbackInfo.getCallback().onBackStarted(mBackEvent);
         waitForIdle();
-        assertTrue(mDispatcher.isDispatching());
+        assertTrue(mDispatcher.isBackGestureInProgress());
         assertTrue(mDispatcher.mTouchTracker.isActive());
 
         main.runWithScissors(() -> mDispatcher.onMotionEvent(mMotionEvent), 100);
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index 03cb17e..1d91af5 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -71,7 +71,7 @@
 
         spyPackageMonitor.register(mMockContext, UserHandle.ALL, mMockHandler);
         assertThat(spyPackageMonitor.getRegisteredHandler()).isEqualTo(mMockHandler);
-        verify(mMockContext, times(1)).registerReceiverAsUser(any(), eq(UserHandle.ALL), any(),
+        verify(mMockContext, never()).registerReceiverAsUser(any(), eq(UserHandle.ALL), any(),
                 eq(null), eq(mMockHandler));
 
         assertThrows(IllegalStateException.class,
@@ -97,7 +97,7 @@
 
     @Test
     public void testPackageMonitorNotRegisterWithoutSupportPackageRestartQuery() throws Exception {
-        PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor(false));
+        PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
 
         spyPackageMonitor.register(mMockContext, UserHandle.ALL, mMockHandler);
 
@@ -106,6 +106,16 @@
     }
 
     @Test
+    public void testPackageMonitorRegisterWithSupportPackageRestartQuery() throws Exception {
+        PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor(true));
+
+        spyPackageMonitor.register(mMockContext, UserHandle.ALL, mMockHandler);
+
+        verify(mMockContext, times(1)).registerReceiverAsUser(any(), eq(UserHandle.ALL), any(),
+                eq(null), eq(mMockHandler));
+    }
+
+    @Test
     public void testPackageMonitorDoHandlePackageEventUidRemoved() throws Exception {
         PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
 
@@ -487,7 +497,7 @@
         }
 
         public TestPackageMonitor() {
-            super();
+            super(false);
         }
     }
 }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 319f115..6d31578 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -465,6 +465,11 @@
      * how pixels are stored. This affects the quality (color depth) as
      * well as the ability to display transparent/translucent colors.
      */
+    // It's touched by Graphics.cpp, so we need to make this enum usable on Ravenwood.
+    // Otherwise, all the ctors would throw, which would make the class unloadable
+    // because the static initializer needs the enum members because of `sConfigs`.
+    // TODO: Remove it once we expose the outer class.
+    @android.ravenwood.annotation.RavenwoodKeepWholeClass
     public enum Config {
         // these native values must match up with the enum in SkBitmap.h
 
diff --git a/graphics/java/android/graphics/Interpolator.java b/graphics/java/android/graphics/Interpolator.java
index 1045464..994fb2d 100644
--- a/graphics/java/android/graphics/Interpolator.java
+++ b/graphics/java/android/graphics/Interpolator.java
@@ -18,6 +18,9 @@
 
 import android.os.SystemClock;
 
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+        android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public class Interpolator {
 
     public Interpolator(int valueCount) {
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index bc58feb..fbb690c 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -28,6 +28,9 @@
 /**
  * The Matrix class holds a 3x3 matrix for transforming coordinates.
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+        android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public class Matrix {
 
     public static final int MSCALE_X = 0;   //!< use with getValues/setValues
@@ -229,7 +232,7 @@
     private static class NoImagePreloadHolder {
         public static final NativeAllocationRegistry sRegistry =
                 NativeAllocationRegistry.createMalloced(
-                Matrix.class.getClassLoader(), nGetNativeFinalizer());
+                Matrix.class.getClassLoader(), nGetNativeFinalizerWrapper());
     }
 
     private final long native_instance;
@@ -238,7 +241,7 @@
      * Create an identity matrix
      */
     public Matrix() {
-        native_instance = nCreate(0);
+        native_instance = nCreateWrapper(0);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
     }
 
@@ -248,7 +251,7 @@
      * @param src The matrix to copy into this matrix
      */
     public Matrix(Matrix src) {
-        native_instance = nCreate(src != null ? src.native_instance : 0);
+        native_instance = nCreateWrapper(src != null ? src.native_instance : 0);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
     }
 
@@ -846,6 +849,34 @@
         return native_instance;
     }
 
+    /**
+     * Wrapper method we use to switch to ExtraNatives.nCreate(src) only on Ravenwood.
+     *
+     * @see ExtraNatives
+     */
+    @android.ravenwood.annotation.RavenwoodReplace
+    private static long nCreateWrapper(long src) {
+        return nCreate(src);
+    }
+
+    private static long nCreateWrapper$ravenwood(long src) {
+        return ExtraNatives.nCreate(src);
+    }
+
+    /**
+     * Wrapper method we use to switch to ExtraNatives.nGetNativeFinalizer(src) only on Ravenwood.
+     *
+     * @see ExtraNatives
+     */
+    @android.ravenwood.annotation.RavenwoodReplace
+    private static long nGetNativeFinalizerWrapper() {
+        return nGetNativeFinalizer();
+    }
+
+    private static long nGetNativeFinalizerWrapper$ravenwood() {
+        return ExtraNatives.nGetNativeFinalizer();
+    }
+
     // ------------------ Regular JNI ------------------------
 
     private static native long nCreate(long nSrc_or_zero);
@@ -943,4 +974,25 @@
     private static native float nMapRadius(long nObject, float radius);
     @CriticalNative
     private static native boolean nEquals(long nA, long nB);
+
+    /**
+     * Due to b/337329128, native methods that are called by the static initializers cannot be
+     * in the same class when running on a host side JVM (such as on Ravenwood and Android Studio).
+     *
+     * There are two methods that are called by the static initializers (either directly or
+     * indirectly) in this class, namely nCreate() and nGetNativeFinalizer(). On Ravenwood
+     * these methods can't be on the Matrix class itself, so we use a nested class to host them.
+     *
+     * We still keep the original nCreate() method and call it on non-ravenwood environment,
+     * in order to avoid problems in downstream (such as Android Studio).
+     *
+     * @see #nCreateWrapper(long)
+     * @see #nGetNativeFinalizerWrapper()
+     *
+     * TODO(b/337110712) Clean it up somehow. (remove the original nCreate() and unify the code?)
+     */
+    private static class ExtraNatives {
+        static native long nCreate(long nSrc_or_zero);
+        static native long nGetNativeFinalizer();
+    }
 }
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index deb89fa..073307c 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -36,11 +36,16 @@
  * (based on the paint's Style), or it can be used for clipping or to draw
  * text on a path.
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+        android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public class Path {
-
-    private static final NativeAllocationRegistry sRegistry =
-            NativeAllocationRegistry.createMalloced(
-                Path.class.getClassLoader(), nGetFinalizer());
+    // See b/337329128 for why we need an inner class here.
+    private static class NoImagePreloadHolder {
+        static final NativeAllocationRegistry sRegistry =
+                NativeAllocationRegistry.createMalloced(
+                        Path.class.getClassLoader(), nGetFinalizer());
+    }
 
     /**
      * @hide
@@ -52,7 +57,7 @@
      */
     public Path() {
         mNativePath = nInit();
-        sRegistry.registerNativeAllocation(this, mNativePath);
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -62,7 +67,7 @@
      */
     public Path(@Nullable Path src) {
         mNativePath = nInit(src != null ? src.mNativePath : 0);
-        sRegistry.registerNativeAllocation(this, mNativePath);
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
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 08b7c01..ecfb134 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
@@ -1515,7 +1515,7 @@
 
         @JvmField
         val DESKTOP_MODE_INITIAL_BOUNDS_SCALE = SystemProperties
-                .getInt("persist.wm.debug.freeform_initial_bounds_scale", 75) / 100f
+                .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
 
         /**
          * Check if desktop density override is enabled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index e7d9812..c5f23a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -373,6 +373,10 @@
 
             if (DesktopModeStatus.isEnabled() && mDesktopModeTaskRepository.isPresent()
                     && mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
+                if (mDesktopModeTaskRepository.get().isMinimizedTask(taskInfo.taskId)) {
+                    // Minimized freeform tasks should not be shown at all.
+                    continue;
+                }
                 // Freeform tasks will be added as a separate entry
                 if (mostRecentFreeformTaskIndex == Integer.MAX_VALUE) {
                     mostRecentFreeformTaskIndex = recentTasks.size();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 40b59c1..5cf9be4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -395,6 +395,51 @@
     }
 
     @Test
+    public void testGetRecentTasks_proto2Enabled_ignoresMinimizedFreeformTasks() {
+        StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
+                DesktopModeStatus.class).startMocking();
+        when(DesktopModeStatus.isEnabled()).thenReturn(true);
+
+        ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+        ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+        ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+        ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+        ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+        setRawList(t1, t2, t3, t4, t5);
+
+        when(mDesktopModeTaskRepository.isActiveTask(1)).thenReturn(true);
+        when(mDesktopModeTaskRepository.isActiveTask(3)).thenReturn(true);
+        when(mDesktopModeTaskRepository.isActiveTask(5)).thenReturn(true);
+        when(mDesktopModeTaskRepository.isMinimizedTask(3)).thenReturn(true);
+
+        ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+                MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+
+        // 2 freeform tasks should be grouped into one, 1 task should be skipped, 3 total recents
+        // entries
+        assertEquals(3, recentTasks.size());
+        GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+        GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
+        GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+
+        // Check that groups have expected types
+        assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+        assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
+        assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+
+        // Check freeform group entries
+        assertEquals(2, freeformGroup.getTaskInfoList().size());
+        assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
+        assertEquals(t5, freeformGroup.getTaskInfoList().get(1));
+
+        // Check single entries
+        assertEquals(t2, singleGroup1.getTaskInfo1());
+        assertEquals(t4, singleGroup2.getTaskInfo1());
+
+        mockitoSession.finishMocking();
+    }
+
+    @Test
     public void testRemovedTaskRemovesSplit() {
         ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
         ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 8315c4c..07e97f8 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -211,11 +211,7 @@
 static jfieldID gRegion_nativeInstanceID;
 static jmethodID gRegion_constructorMethodID;
 
-static jclass    gByte_class;
-static jobject   gVMRuntime;
-static jclass    gVMRuntime_class;
-static jmethodID gVMRuntime_newNonMovableArray;
-static jmethodID gVMRuntime_addressOf;
+static jclass gByte_class;
 
 static jclass gColorSpace_class;
 static jmethodID gColorSpace_getMethodID;
@@ -789,13 +785,6 @@
     gByte_class = (jclass) env->NewGlobalRef(
         env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;")));
 
-    gVMRuntime_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "dalvik/system/VMRuntime"));
-    m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
-    gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m));
-    gVMRuntime_newNonMovableArray = GetMethodIDOrDie(env, gVMRuntime_class, "newNonMovableArray",
-                                                     "(Ljava/lang/Class;I)Ljava/lang/Object;");
-    gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
-
     gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
     gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
             "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index ca667b0..c0d791a 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -376,11 +376,24 @@
     {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
 };
 
+static const JNINativeMethod extra_methods[] = {
+        {"nGetNativeFinalizer", "()J", (void*)SkMatrixGlue::getNativeFinalizer},
+        {"nCreate", "(J)J", (void*)SkMatrixGlue::create},
+};
+
 static jclass sClazz;
 static jfieldID sNativeInstanceField;
 static jmethodID sCtor;
 
 int register_android_graphics_Matrix(JNIEnv* env) {
+    // Methods only used on Ravenwood (for now). See the javadoc on Matrix$ExtraNativesx
+    // for why we need it.
+    //
+    // We don't need it on non-ravenwood, but we don't (yet) have a way to detect ravenwood
+    // environment, so we just always run it.
+    RegisterMethodsOrDie(env, "android/graphics/Matrix$ExtraNatives", extra_methods,
+                         NELEM(extra_methods));
+
     int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods));
 
     jclass clazz = FindClassOrDie(env, "android/graphics/Matrix");
diff --git a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
new file mode 100644
index 0000000..0b39a9a
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.location.provider;
+
+import android.annotation.Nullable;
+import android.app.trust.TrustManager;
+import android.hardware.location.ISignificantPlaceProvider;
+import android.hardware.location.ISignificantPlaceProviderManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+/** @hide */
+public class SignificantPlaceProvider {
+
+    public static final String ACTION = TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER;
+
+    private final IBinder mBinder;
+
+    // write locked on mBinder, read lock is optional depending on atomicity requirements
+    @Nullable private volatile ISignificantPlaceProviderManager mManager;
+
+    @GuardedBy("mBinder")
+    private boolean mInSignificantPlace = false;
+
+    public SignificantPlaceProvider() {
+        mBinder = new Service();
+        mManager = null;
+    }
+
+    public IBinder getBinder() {
+        return mBinder;
+    }
+
+    /** Set whether the device is currently in a trusted location. */
+    public void setInSignificantPlace(boolean inSignificantPlace) {
+        synchronized (mBinder) {
+            if (inSignificantPlace == mInSignificantPlace) {
+                return;
+            }
+
+            mInSignificantPlace = inSignificantPlace;
+        }
+
+        ISignificantPlaceProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.setInSignificantPlace(inSignificantPlace);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private final class Service extends ISignificantPlaceProvider.Stub {
+
+        Service() {}
+
+        @Override
+        public void setSignificantPlaceProviderManager(ISignificantPlaceProviderManager manager) {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                return;
+            }
+
+            synchronized (mBinder) {
+                if (mInSignificantPlace) {
+                    try {
+                        manager.setInSignificantPlace(true);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+
+                mManager = manager;
+            }
+        }
+    }
+}
diff --git a/media/OWNERS b/media/OWNERS
index 1e5a458..5e39195 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -11,6 +11,7 @@
 nchalko@google.com
 philburk@google.com
 quxiangfang@google.com
+shuzhenwang@google.com
 wonsik@google.com
 
 # go/android-fwk-media-solutions for info on areas of ownership.
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/OWNERS b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/OWNERS
new file mode 100644
index 0000000..f4f9c08
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 1344
+include platform/frameworks/base:/media/OWNERS
+
+# Camera
+per-file *Camera* = file:platform/frameworks/av:/camera/OWNERS
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 169c330..57bde56 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -29,6 +29,7 @@
 import android.os.UserManager;
 import android.permission.PermissionManager;
 import android.text.format.DateUtils;
+import android.util.ArrayMap;
 import android.util.IconDrawableFactory;
 import android.util.Log;
 
@@ -127,6 +128,7 @@
         final long now = mClock.millis();
         final UserManager um = mContext.getSystemService(UserManager.class);
         final List<UserHandle> profiles = um.getUserProfiles();
+        ArrayMap<UserHandle, Boolean> shouldIncludeAppsByUsers = new ArrayMap<>();
 
         for (int i = 0; i < appOpsCount; ++i) {
             AppOpsManager.PackageOps ops = appOps.get(i);
@@ -134,9 +136,13 @@
             int uid = ops.getUid();
             UserHandle user = UserHandle.getUserHandleForUid(uid);
 
+            if (!shouldIncludeAppsByUsers.containsKey(user)) {
+                shouldIncludeAppsByUsers.put(user, shouldHideUser(um, user));
+            }
+
             // Don't show apps belonging to background users except for profiles that shouldn't
             // be shown in quiet mode.
-            if (!profiles.contains(user) || isHideInQuietEnabledForProfile(um, user)) {
+            if (!profiles.contains(user) || !shouldIncludeAppsByUsers.get(user)) {
                 continue;
             }
 
@@ -200,7 +206,7 @@
         return accesses;
     }
 
-    private boolean isHideInQuietEnabledForProfile(UserManager userManager, UserHandle userHandle) {
+    private boolean shouldHideUser(UserManager userManager, UserHandle userHandle) {
         if (android.multiuser.Flags.enablePrivateSpaceFeatures()
                 && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
             return userManager.isQuietModeEnabled(userHandle)
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e2af631..65c5708 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -69,6 +69,219 @@
     visibility: ["//visibility:private"],
 }
 
+filegroup {
+    name: "SystemUI-tests-robofiles",
+    srcs: [
+        "tests/src/**/*.kt",
+        "tests/src/**/*.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+// We are running robolectric tests in the tests directory as well as
+// multivalent tests.  If you add a test, and it doesn't run in robolectric,
+// it should be added to this exclusion list. go/multivalent-tests
+filegroup {
+    name: "SystemUI-tests-broken-robofiles",
+    srcs: [
+        "tests/src/**/*DeviceOnlyTest.java",
+        "tests/src/**/*DeviceOnlyTest.kt",
+        "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt",
+        "tests/src/**/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt",
+        "tests/src/**/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt",
+        "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt",
+        "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt",
+        "tests/src/**/systemui/controls/start/ControlsStartableTest.kt",
+        "tests/src/**/systemui/haptics/slider/SliderStateTrackerTest.kt",
+        "tests/src/**/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt",
+        "tests/src/**/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt",
+        "tests/src/**/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt",
+        "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt",
+        "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
+        "tests/src/**/systemui/keyguard/ResourceTrimmerTest.kt",
+        "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt",
+        "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt",
+        "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt",
+        "tests/src/**/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt",
+        "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt",
+        "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt",
+        "tests/src/**/systemui/qs/tiles/DeviceControlsTileTest.kt",
+        "tests/src/**/systemui/screenshot/ActionExecutorTest.kt",
+        "tests/src/**/systemui/screenshot/ActionIntentCreatorTest.kt",
+        "tests/src/**/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt",
+        "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt",
+        "tests/src/**/systemui/statusbar/commandline/CommandRegistryTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt",
+        "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt",
+        "tests/src/**/systemui/statusbar/policy/BatteryStateNotifierTest.kt",
+        "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt",
+        "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt",
+        "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
+        "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
+        "tests/src/**/keyguard/ClockEventControllerTest.kt",
+        "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt",
+        "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt",
+        "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java",
+        "tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt",
+        "tests/src/**/systemui/broadcast/UserBroadcastDispatcherTest.kt",
+        "tests/src/**/systemui/charging/WiredChargingRippleControllerTest.kt",
+        "tests/src/**/systemui/clipboardoverlay/ClipboardModelTest.kt",
+        "tests/src/**/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt",
+        "tests/src/**/systemui/controls/controller/ControlsBindingControllerImplTest.kt",
+        "tests/src/**/systemui/controls/controller/ControlsControllerImplTest.kt",
+        "tests/src/**/systemui/controls/controller/DeletionJobServiceTest.kt",
+        "tests/src/**/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt",
+        "tests/src/**/systemui/controls/ui/ControlsUiControllerImplTest.kt",
+        "tests/src/**/systemui/controls/ui/ControlViewHolderTest.kt",
+        "tests/src/**/systemui/controls/ui/SelectionItemTest.kt",
+        "tests/src/**/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt",
+        "tests/src/**/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt",
+        "tests/src/**/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt",
+        "tests/src/**/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt",
+        "tests/src/**/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt",
+        "tests/src/**/systemui/media/controls/ui/controller/MediaControlPanelTest.kt",
+        "tests/src/**/systemui/media/controls/ui/controller/MediaViewControllerTest.kt",
+        "tests/src/**/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt",
+        "tests/src/**/systemui/media/controls/ui/MediaPlayerDataTest.kt",
+        "tests/src/**/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt",
+        "tests/src/**/systemui/navigationbar/gestural/BackPanelControllerTest.kt",
+        "tests/src/**/systemui/notetask/NoteTaskControllerTest.kt",
+        "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt",
+        "tests/src/**/systemui/power/domain/interactor/PowerInteractorTest.kt",
+        "tests/src/**/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt",
+        "tests/src/**/systemui/privacy/PrivacyItemControllerTest.kt",
+        "tests/src/**/systemui/qs/external/CustomTileStatePersisterTest.kt",
+        "tests/src/**/systemui/qs/external/TileRequestDialogTest.kt",
+        "tests/src/**/systemui/qs/external/TileServiceRequestControllerTest.kt",
+        "tests/src/**/systemui/qs/tileimpl/TilesStatesTextTest.kt",
+        "tests/src/**/systemui/qs/tiles/AlarmTileTest.kt",
+        "tests/src/**/systemui/qs/tiles/BluetoothTileTest.kt",
+        "tests/src/**/systemui/screenshot/ScreenshotPolicyImplTest.kt",
+        "tests/src/**/systemui/settings/DisplayTrackerImplTest.kt",
+        "tests/src/**/systemui/settings/UserFileManagerImplTest.kt",
+        "tests/src/**/systemui/settings/UserTrackerImplReceiveTest.kt",
+        "tests/src/**/systemui/settings/UserTrackerImplTest.kt",
+        "tests/src/**/systemui/shade/GlanceableHubContainerControllerTest.kt",
+        "tests/src/**/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt",
+        "tests/src/**/systemui/shade/NotificationsQSContainerControllerTest.kt",
+        "tests/src/**/systemui/shade/ShadeExpansionStateManagerTest.kt",
+        "tests/src/**/systemui/shade/ShadeHeaderControllerTest.kt",
+        "tests/src/**/systemui/shade/transition/LargeScreenShadeInterpolatorImplTest.kt",
+        "tests/src/**/systemui/statusbar/commandline/CommandParserTest.kt",
+        "tests/src/**/systemui/statusbar/connectivity/MobileStateTest.kt",
+        "tests/src/**/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt",
+        "tests/src/**/systemui/statusbar/gesture/GenericGestureDetectorTest.kt",
+        "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt",
+        "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt",
+        "tests/src/**/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt",
+        "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt",
+        "tests/src/**/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/RoundableTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt",
+        "tests/src/**/systemui/statusbar/notification/row/TextPrecomputerTest.kt",
+        "tests/src/**/systemui/statusbar/phone/FoldStateListenerTest.kt",
+        "tests/src/**/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt",
+        "tests/src/**/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt",
+        "tests/src/**/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt",
+        "tests/src/**/systemui/statusbar/policy/VariableDateViewControllerTest.kt",
+        "tests/src/**/systemui/statusbar/policy/WalletControllerImplTest.kt",
+        "tests/src/**/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt",
+        "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
+        "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
+        "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
+        "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java",
+        "tests/src/**/keyguard/CarrierTextManagerTest.java",
+        "tests/src/**/systemui/ScreenDecorationsTest.java",
+        "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+        "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
+        "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
+        "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt",
+        "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
+        "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
+        "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
+        "tests/src/**/systemui/DisplayCutoutBaseViewTest.kt",
+        "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java",
+        "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java",
+        "tests/src/**/systemui/qs/tiles/HotspotTileTest.java",
+        "tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java",
+        "tests/src/**/systemui/recents/OverviewProxyServiceTest.kt",
+        "tests/src/**/systemui/stylus/StylusManagerTest.kt",
+        "tests/src/**/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java",
+        "tests/src/**/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java",
+        "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
+        "tests/src/**/systemui/statusbar/policy/BatteryControllerTest.java",
+        "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt",
+        "tests/src/**/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt",
+        "tests/src/**/systemui/statusbar/KeyboardShortcutsReceiverTest.java",
+        "tests/src/**/systemui/wmshell/BubblesTest.java",
+        "tests/src/**/systemui/biometrics/AuthRippleControllerTest.kt",
+        "tests/src/**/keyguard/KeyguardAbsKeyInputViewControllerTest.java",
+        "tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java",
+        "tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java",
+        "tests/src/**/systemui/doze/DozeScreenStateTest.java",
+        "tests/src/**/systemui/keyguard/WorkLockActivityControllerTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputControllerTest.java",
+        "tests/src/**/systemui/navigationbar/NavigationBarTest.java",
+        "tests/src/**/systemui/power/PowerNotificationWarningsTest.java",
+        "tests/src/**/systemui/power/PowerUITest.java",
+        "tests/src/**/systemui/qs/QSFooterViewControllerTest.java",
+        "tests/src/**/systemui/qs/QSImplTest.java",
+        "tests/src/**/systemui/qs/QSSecurityFooterTest.java",
+        "tests/src/**/systemui/qs/tileimpl/QSTileImplTest.java",
+        "tests/src/**/systemui/qs/tiles/QuickAccessWalletTileTest.java",
+        "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java",
+        "tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java",
+        "tests/src/**/systemui/statusbar/CommandQueueTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/CallbackHandlerTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerBaseTest.java",
+        "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java",
+        "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java",
+        "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java",
+        "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java",
+        "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java",
+        "tests/src/**/systemui/toast/ToastUITest.java",
+        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerDataTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java",
+        "tests/src/**/systemui/statusbar/connectivity/NetworkControllerWifiTest.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
 //Create a library to expose SystemUI's resources to other modules.
 android_library {
     name: "SystemUI-res",
@@ -433,6 +646,21 @@
     plugins: ["dagger2-compiler"],
 }
 
+java_library {
+    name: "RoboTestLibraries",
+    static_libs: [
+        "dagger2",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.core_core-animation-testing",
+        "androidx.test.ext.junit",
+        "inline-mockito-robolectric-prebuilt",
+        "platform-parametric-runner-lib",
+        "SystemUICustomizationTestUtils",
+        "kotlin-test",
+        "kosmos",
+    ],
+}
+
 android_robolectric_test {
     name: "SystemUiRoboTests",
     srcs: [
@@ -442,14 +670,40 @@
         ":SystemUI-tests-multivalent",
     ],
     static_libs: [
-        "dagger2",
-        "androidx.test.uiautomator_uiautomator",
-        "androidx.core_core-animation-testing",
-        "androidx.test.ext.junit",
-        "inline-mockito-robolectric-prebuilt",
-        "platform-parametric-runner-lib",
-        "SystemUICustomizationTestUtils",
-        "kosmos",
+        "RoboTestLibraries",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+        "truth",
+    ],
+
+    upstream: true,
+
+    instrumentation_for: "SystemUIRobo-stub",
+    java_resource_dirs: ["tests/robolectric/config"],
+    plugins: [
+        "dagger2-compiler",
+    ],
+}
+
+// in-place tests which use Robolectric in the tests directory
+// instead of multivalentTests
+android_robolectric_test {
+    name: "SystemUiRoboTestsInplace",
+    srcs: [
+        "tests/robolectric/src/**/*.kt",
+        "tests/robolectric/src/**/*.java",
+        ":SystemUI-tests-utils",
+        ":SystemUI-tests-multivalent",
+        ":SystemUI-tests-robofiles",
+    ],
+    exclude_srcs: [
+        ":SystemUI-tests-broken-robofiles",
+    ],
+    static_libs: [
+        "RoboTestLibraries",
     ],
     libs: [
         "android.test.runner",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index 9ad0fc5..fd79f62 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -191,14 +191,20 @@
         // so we have to take the optical insets into account.
         ghostedView.getLocationOnScreen(ghostedViewLocation)
         val insets = backgroundInsets
-        state.top = ghostedViewLocation[1] + insets.top
+        val boundCorrections: Rect =
+            if (ghostedView is LaunchableView) {
+                ghostedView.getPaddingForLaunchAnimation()
+            } else {
+                Rect()
+            }
+        state.top = ghostedViewLocation[1] + insets.top + boundCorrections.top
         state.bottom =
             ghostedViewLocation[1] + (ghostedView.height * ghostedView.scaleY).roundToInt() -
-                insets.bottom
-        state.left = ghostedViewLocation[0] + insets.left
+                insets.bottom + boundCorrections.bottom
+        state.left = ghostedViewLocation[0] + insets.left + boundCorrections.left
         state.right =
             ghostedViewLocation[0] + (ghostedView.width * ghostedView.scaleX).roundToInt() -
-                insets.right
+                insets.right + boundCorrections.right
     }
 
     override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
index da6ccaa..330ab0f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.animation
 
+import android.graphics.Rect
 import android.view.View
 
 /** A view that can expand/launch into an app or a dialog. */
@@ -41,6 +42,9 @@
 
     /** Perform an action when the activity launch animation ends */
     fun onActivityLaunchAnimationEnd() {}
+
+    /** Provide an optional correction applied to the visible area during a launch animation */
+    fun getPaddingForLaunchAnimation(): Rect = Rect()
 }
 
 /** A delegate that can be used by views to make the implementation of [LaunchableView] easier. */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 985d3a1..6e987bd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -65,7 +65,6 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.modifiers.height
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
@@ -157,6 +156,7 @@
 fun SceneScope.NotificationScrollingStack(
     viewModel: NotificationsPlaceholderViewModel,
     maxScrimTop: () -> Float,
+    shouldPunchHoleBehindScrim: Boolean,
     modifier: Modifier = Modifier,
 ) {
     val density = LocalDensity.current
@@ -233,7 +233,8 @@
                     // in step with the transition so that it is 0 when it completes.
                     if (
                         scrimOffset.value < 0 &&
-                            layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Gone)
+                            layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Gone) ||
+                            layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen)
                     ) {
                         IntOffset(x = 0, y = (scrimOffset.value * expansionFraction).roundToInt())
                     } else {
@@ -246,7 +247,7 @@
                                 scrimCornerRadius,
                                 screenCornerRadius,
                                 { expansionFraction },
-                                layoutState.isNotificationScrimTransitioning(),
+                                shouldPunchHoleBehindScrim,
                             )
                             .let { scrimRounding.value.toRoundedCornerShape(it) }
                     clip = true
@@ -270,18 +271,20 @@
     ) {
         // Creates a cutout in the background scrim in the shape of the notifications scrim.
         // Only visible when notif scrim alpha < 1, during shade expansion.
-        Spacer(
-            modifier =
-                Modifier.fillMaxSize().drawBehind {
-                    drawRect(Color.Black, blendMode = BlendMode.DstOut)
-                }
-        )
+        if (shouldPunchHoleBehindScrim) {
+            Spacer(
+                modifier =
+                    Modifier.fillMaxSize().drawBehind {
+                        drawRect(Color.Black, blendMode = BlendMode.DstOut)
+                    }
+            )
+        }
         Box(
             modifier =
                 Modifier.fillMaxSize()
                     .graphicsLayer {
                         alpha =
-                            if (layoutState.isNotificationScrimTransitioning()) {
+                            if (shouldPunchHoleBehindScrim) {
                                 (expansionFraction / EXPANSION_FOR_MAX_SCRIM_ALPHA).coerceAtMost(1f)
                             } else 1f
                     }
@@ -429,13 +432,6 @@
     )
 }
 
-private fun SceneTransitionLayoutState.isNotificationScrimTransitioning(): Boolean {
-    return isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
-        isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade) ||
-        isTransitioningBetween(Scenes.Gone, Scenes.QuickSettings) ||
-        isTransitioningBetween(Scenes.Lockscreen, Scenes.QuickSettings)
-}
-
 private const val TAG = "FlexiNotifs"
 private val DEBUG_STACK_COLOR = Color(1f, 0f, 0f, 0.2f)
 private val DEBUG_HUN_COLOR = Color(0f, 0f, 1f, 0.2f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 62619f5..b33ea78 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -61,6 +61,7 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
@@ -155,15 +156,22 @@
         qsSceneAdapter = viewModel.qsSceneAdapter
     )
 
+    val shouldPunchHoleBehindScrim =
+        layoutState.isTransitioningBetween(Scenes.Gone, Scenes.QuickSettings) ||
+            layoutState.isTransitioningBetween(Scenes.Lockscreen, Scenes.QuickSettings)
+
     // TODO(b/280887232): implement the real UI.
     Box(
         modifier =
-            modifier.fillMaxSize().graphicsLayer {
-                // Render the scene to an offscreen buffer so that BlendMode.DstOut only clears this
-                // scene (and not the one under it) during a scene transition.
-                compositingStrategy = CompositingStrategy.Offscreen
-                alpha = contentAlpha
-            }
+            modifier
+                .fillMaxSize()
+                .graphicsLayer { alpha = contentAlpha }
+                .thenIf(shouldPunchHoleBehindScrim) {
+                    // Render the scene to an offscreen buffer so that BlendMode.DstOut only clears
+                    // this
+                    // scene (and not the one under it) during a scene transition.
+                    Modifier.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
+                }
     ) {
         val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
         val screenHeight = LocalRawScreenHeight.current
@@ -313,6 +321,7 @@
         NotificationScrollingStack(
             viewModel = notificationsPlaceholderViewModel,
             maxScrimTop = { screenHeight },
+            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
             modifier =
                 Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
         )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 4c656b0..19c5800c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -16,6 +16,7 @@
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
+import com.android.systemui.shade.ui.composable.Shade
 
 /**
  * Comprehensive definition of all transitions between scenes in [SceneContainer].
@@ -73,10 +74,14 @@
 
     // Scene overscroll
 
+    overscroll(Scenes.Gone, Orientation.Vertical) {}
     overscroll(Scenes.Bouncer, Orientation.Vertical) {
         translate(Bouncer.Elements.Content, y = { absoluteDistance })
     }
     overscroll(Scenes.Shade, Orientation.Vertical) {
-        translate(Notifications.Elements.NotificationScrim, y = { absoluteDistance })
+        translate(
+            Notifications.Elements.NotificationScrim,
+            y = { Shade.Dimensions.ScrimOverscrollLimit }
+        )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
index a9e5be9..8cd357e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
@@ -1,10 +1,15 @@
 package com.android.systemui.scene.ui.composable.transitions
 
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ui.composable.ShadeHeader
 import kotlin.time.Duration.Companion.milliseconds
 
@@ -12,6 +17,18 @@
     durationScale: Double = 1.0,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+    distance =
+        object : UserActionDistance {
+            override fun UserActionDistanceScope.absoluteDistance(
+                fromSceneSize: IntSize,
+                orientation: Orientation,
+            ): Float {
+                val distance =
+                    Notifications.Elements.NotificationScrim.targetOffset(Scenes.Shade)?.y
+                        ?: return 0f
+                return fromSceneSize.height - distance
+            }
+        }
 
     translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
     timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
index 2f59217..df47cba 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
@@ -16,11 +16,19 @@
 
 package com.android.systemui.scene.ui.composable.transitions
 
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ui.composable.Shade
 import com.android.systemui.shade.ui.composable.ShadeHeader
 import kotlin.time.Duration.Companion.milliseconds
 
@@ -28,6 +36,20 @@
     durationScale: Double = 1.0,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+    swipeSpec =
+        spring(
+            stiffness = Spring.StiffnessMediumLow,
+            visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
+        )
+    distance =
+        object : UserActionDistance {
+            override fun UserActionDistanceScope.absoluteDistance(
+                fromSceneSize: IntSize,
+                orientation: Orientation,
+            ): Float {
+                return Notifications.Elements.NotificationScrim.targetOffset(Scenes.Shade)?.y ?: 0f
+            }
+        }
 
     fractionRange(start = .58f) {
         fade(ShadeHeader.Elements.Clock)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index cda8059..3122b99 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -98,6 +98,8 @@
     object Dimensions {
         val ScrimCornerSize = 32.dp
         val HorizontalPadding = 16.dp
+        const val ScrimOverscrollLimit = 100f
+        const val ScrimVisibilityThreshold = 5f
     }
 
     object Shapes {
@@ -195,12 +197,26 @@
 ) {
     val maxNotifScrimTop = remember { mutableStateOf(0f) }
     val tileSquishiness by
-        animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
+        animateSceneFloatAsState(
+            value = 1f,
+            key = QuickSettings.SharedValues.TilesSquishiness,
+            canOverflow = false
+        )
     val isClickable by viewModel.isClickable.collectAsState()
 
-    // Render the scene to an offscreen buffer so that BlendMode.DstOut only clears this scene
-    // (and not the one under it) during a scene transition.
-    Box(modifier = modifier.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)) {
+    val shouldPunchHoleBehindScrim =
+        layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
+            layoutState.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
+
+    Box(
+        modifier =
+            modifier.thenIf(shouldPunchHoleBehindScrim) {
+                // Render the scene to an offscreen buffer so that BlendMode.DstOut only clears this
+                // scene
+                // (and not the one under it) during a scene transition.
+                Modifier.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
+            }
+    ) {
         Box(
             modifier =
                 Modifier.fillMaxSize()
@@ -231,10 +247,7 @@
                             Box(Modifier.element(QuickSettings.Elements.QuickQuickSettings)) {
                                 QuickSettings(
                                     viewModel.qsSceneAdapter,
-                                    {
-                                        (viewModel.qsSceneAdapter.qqsHeight * tileSquishiness)
-                                            .roundToInt()
-                                    },
+                                    { viewModel.qsSceneAdapter.qqsHeight },
                                     isSplitShade = false,
                                     squishiness = tileSquishiness,
                                 )
@@ -254,6 +267,7 @@
                         NotificationScrollingStack(
                             viewModel = viewModel.notifications,
                             maxScrimTop = { maxNotifScrimTop.value },
+                            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
                         )
                     },
                 )
@@ -425,6 +439,7 @@
                 NotificationScrollingStack(
                     viewModel = viewModel.notifications,
                     maxScrimTop = { 0f },
+                    shouldPunchHoleBehindScrim = false,
                     modifier =
                         Modifier.weight(1f)
                             .fillMaxHeight()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
index c73656e..f1cc71b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.volume.panel.component.mediaoutput
 
-import com.android.systemui.volume.panel.component.mediaoutput.domain.MediaOutputAvailabilityCriteria
 import com.android.systemui.volume.panel.component.mediaoutput.ui.composable.MediaOutputComponent
 import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.domain.AlwaysAvailableCriteria
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
 import dagger.Binds
@@ -39,6 +39,6 @@
     @IntoMap
     @StringKey(VolumePanelComponents.MEDIA_OUTPUT)
     fun bindComponentAvailabilityCriteria(
-        criteria: MediaOutputAvailabilityCriteria
+        criteria: AlwaysAvailableCriteria
     ): ComponentAvailabilityCriteria
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 9c0674d..bc57ce6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -245,6 +245,33 @@
             assertThat(flowForUser2).isNotEqualTo(flowForUser1)
         }
 
+    // Tests that a ServiceInfo that is returned by queryIntentServicesAsUser but shortly
+    // after uninstalled, doesn't crash SystemUI.
+    @Test
+    fun packageUninstalledAfterQuery_noCrash_noComponent() =
+        testScope.runTest {
+            val userId = 0
+            val resolveInfo =
+                ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true)
+
+            val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+
+            whenever(
+                    packageManager.queryIntentServicesAsUser(
+                        matchIntent(),
+                        matchFlags(),
+                        eq(userId)
+                    )
+                )
+                .thenReturn(listOf(resolveInfo))
+            whenever(packageManager.getComponentEnabledSetting(TEST_COMPONENT))
+                .thenThrow(IllegalArgumentException())
+            kosmos.fakePackageChangeRepository.notifyChange(PackageChangeModel.Empty)
+            runCurrent()
+
+            assertThat(componentNames).isEmpty()
+        }
+
     companion object {
         private val INTENT = Intent(TileService.ACTION_QS_TILE)
         private val FLAGS =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
deleted file mode 100644
index 96b4dae..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.mediaoutput.domain
-
-import android.media.AudioManager
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.android.systemui.volume.data.repository.audioRepository
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@RunWithLooper(setAsMainLooper = true)
-class MediaOutputAvailabilityCriteriaTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-
-    private lateinit var underTest: MediaOutputAvailabilityCriteria
-
-    @Before
-    fun setup() {
-        underTest =
-            MediaOutputAvailabilityCriteria(
-                kosmos.audioModeInteractor,
-            )
-    }
-
-    @Test
-    fun notInCall_isAvailable_true() {
-        with(kosmos) {
-            testScope.runTest {
-                audioRepository.setMode(AudioManager.MODE_NORMAL)
-
-                val isAvailable by collectLastValue(underTest.isAvailable())
-                runCurrent()
-
-                assertThat(isAvailable).isTrue()
-            }
-        }
-    }
-
-    @Test
-    fun inCall_isAvailable_false() {
-        with(kosmos) {
-            testScope.runTest {
-                audioRepository.setMode(AudioManager.MODE_IN_CALL)
-
-                val isAvailable by collectLastValue(underTest.isAvailable())
-                runCurrent()
-
-                assertThat(isAvailable).isFalse()
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
index b5c5809..64c9429 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.volume.localMediaController
 import com.android.systemui.volume.mediaControllerRepository
 import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.shared.model.filterData
 import com.android.systemui.volume.remoteMediaController
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -67,7 +68,8 @@
     fun playbackInfo_returnsPlaybackInfo() {
         with(kosmos) {
             testScope.runTest {
-                val session by collectLastValue(mediaOutputInteractor.defaultActiveMediaSession)
+                val session by
+                    collectLastValue(mediaOutputInteractor.defaultActiveMediaSession.filterData())
                 runCurrent()
                 val info by collectLastValue(underTest.playbackInfo(session!!))
                 runCurrent()
@@ -81,7 +83,8 @@
     fun playbackState_returnsPlaybackState() {
         with(kosmos) {
             testScope.runTest {
-                val session by collectLastValue(mediaOutputInteractor.defaultActiveMediaSession)
+                val session by
+                    collectLastValue(mediaOutputInteractor.defaultActiveMediaSession.filterData())
                 runCurrent()
                 val state by collectLastValue(underTest.playbackState(session!!))
                 runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index 30524d9..49f82d4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -30,6 +30,8 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.domain.interactor.audioModeInteractor
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
 import com.android.systemui.volume.localMediaController
 import com.android.systemui.volume.localMediaRepository
 import com.android.systemui.volume.mediaControllerRepository
@@ -64,6 +66,8 @@
                     testScope.backgroundScope,
                     mediaOutputActionsInteractor,
                     mediaDeviceSessionInteractor,
+                    audioOutputInteractor,
+                    audioModeInteractor,
                     mediaOutputInteractor,
                     uiEventLogger,
                 )
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 40d863d..dcdd4f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1634,12 +1634,15 @@
     <!-- Hint for accessibility. A stream name is a parameter. For example: double tap to unmute media [CHAR_LIMIT=NONE] -->
     <string name="volume_panel_hint_unmute">unmute %s</string>
 
-    <!-- Title with application label for media output settings. [CHAR LIMIT=20] -->
+    <!-- Title with application label for media output settings when there is media playing. [CHAR LIMIT=20] -->
     <string name="media_output_label_title">Playing <xliff:g id="label" example="Music Player">%s</xliff:g> on</string>
 
     <!-- Title for media output settings without media is playing. [CHAR LIMIT=20] -->
     <string name="media_output_title_without_playing">Audio will play on</string>
 
+    <!-- Title for media output settings when there is an ongoing call in progress. [CHAR LIMIT=20] -->
+    <string name="media_output_title_ongoing_call">Calling on</string>
+
     <!-- Name of special SystemUI debug settings -->
     <string name="system_ui_tuner">System UI Tuner</string>
 
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 6e1b670..660f0db 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
@@ -68,6 +68,7 @@
 import java.io.PrintWriter;
 import java.util.Optional;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.function.Supplier;
 
 /**
@@ -199,6 +200,10 @@
         return mContext;
     }
 
+    /**
+     * We should pass single threaded executor (rather than {@link ThreadPoolExecutor}) as we will
+     * make binder calls on that executor and ordering is vital.
+     */
     public void setBgExecutor(Executor bgExecutor) {
         mBgExecutor = bgExecutor;
     }
@@ -230,25 +235,22 @@
         mListenersRegistered = true;
 
         mBgExecutor.execute(() -> {
+            if (registerRotationWatcher) {
+                try {
+                    WindowManagerGlobal.getWindowManagerService()
+                            .watchRotation(mRotationWatcher, DEFAULT_DISPLAY);
+                    mRotationWatcherRegistered = true;
+                } catch (IllegalArgumentException e) {
+                    Log.w(TAG, "RegisterListeners for the display failed", e);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RegisterListeners caught a RemoteException", e);
+                }
+            }
             final Intent intent = mContext.registerReceiver(mDockedReceiver,
                     new IntentFilter(Intent.ACTION_DOCK_EVENT));
             mContext.getMainExecutor().execute(() -> updateDockedState(intent));
         });
 
-        if (registerRotationWatcher) {
-            try {
-                WindowManagerGlobal.getWindowManagerService()
-                        .watchRotation(mRotationWatcher, DEFAULT_DISPLAY);
-                mRotationWatcherRegistered = true;
-            } catch (IllegalArgumentException e) {
-                mListenersRegistered = false;
-                Log.w(TAG, "RegisterListeners for the display failed", e);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RegisterListeners caught a RemoteException", e);
-                return;
-            }
-        }
-
         TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
     }
 
@@ -265,17 +267,16 @@
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Docked receiver already unregistered", e);
             }
-        });
 
-        if (mRotationWatcherRegistered) {
-            try {
-                WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(
-                        mRotationWatcher);
-            } catch (RemoteException e) {
-                Log.e(TAG, "UnregisterListeners caught a RemoteException", e);
-                return;
+            if (mRotationWatcherRegistered) {
+                try {
+                    WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(
+                            mRotationWatcher);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "UnregisterListeners caught a RemoteException", e);
+                }
             }
-        }
+        });
 
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
index c591af2..338c53b 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
@@ -57,6 +57,7 @@
                                     qsLongPressEffect.clearActionType()
                                 }
                                 QSLongPressEffect.ActionType.LONG_PRESS -> {
+                                    tile.prepareForLaunch()
                                     tile.performLongClick()
                                     qsLongPressEffect.clearActionType()
                                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index cccb93c..b0d45ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -25,6 +25,7 @@
 import androidx.core.view.isInvisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launch
 import com.android.systemui.common.ui.view.LongPressHandlingView
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
@@ -40,6 +41,7 @@
 
 @ExperimentalCoroutinesApi
 object DeviceEntryIconViewBinder {
+    private const val TAG = "DeviceEntryIconViewBinder"
 
     /**
      * Updates UI for:
@@ -82,22 +84,22 @@
             // GONE => AOD transition (even though the view may not be visible until the middle
             // of the transition.
             repeatOnLifecycle(Lifecycle.State.CREATED) {
-                launch {
+                launch("$TAG#viewModel.isVisible") {
                     viewModel.isVisible.collect { isVisible ->
                         longPressHandlingView.isInvisible = !isVisible
                     }
                 }
-                launch {
+                launch("$TAG#viewModel.isLongPressEnabled") {
                     viewModel.isLongPressEnabled.collect { isEnabled ->
                         longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
                     }
                 }
-                launch {
+                launch("$TAG#viewModel.accessibilityDelegateHint") {
                     viewModel.accessibilityDelegateHint.collect { hint ->
                         view.accessibilityHintType = hint
                     }
                 }
-                launch {
+                launch("$TAG#viewModel.useBackgroundProtection") {
                     viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
                         if (useBackgroundProtection) {
                             bgView.visibility = View.VISIBLE
@@ -106,7 +108,7 @@
                         }
                     }
                 }
-                launch {
+                launch("$TAG#viewModel.burnInOffsets") {
                     viewModel.burnInOffsets.collect { burnInOffsets ->
                         view.translationX = burnInOffsets.x.toFloat()
                         view.translationY = burnInOffsets.y.toFloat()
@@ -114,7 +116,9 @@
                     }
                 }
 
-                launch { viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } }
+                launch("$TAG#viewModel.deviceEntryViewAlpha") {
+                    viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
+                }
             }
         }
 
@@ -122,7 +126,7 @@
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 // Start with an empty state
                 fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
-                launch {
+                launch("$TAG#fpIconView.viewModel") {
                     fgViewModel.viewModel.collect { viewModel ->
                         fgIconView.setImageState(
                             view.getIconState(viewModel.type, viewModel.useAodVariant),
@@ -144,8 +148,10 @@
 
         bgView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
-                launch { bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } }
-                launch {
+                launch("$TAG#bgViewModel.alpha") {
+                    bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha }
+                }
+                launch("$TAG#bgViewModel.color") {
                     bgViewModel.color.collect { color ->
                         bgView.imageTintList = ColorStateList.valueOf(color)
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 45dca99..e26b75f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -50,6 +50,7 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
 
 /** Models the UI state for the containing device entry icon & long-press handling view. */
 @ExperimentalCoroutinesApi
@@ -110,27 +111,7 @@
             )
         }
 
-    private val dozeAmount: Flow<Float> =
-        combine(
-            transitionInteractor.startedKeyguardTransitionStep,
-            merge(
-                transitionInteractor.transitionStepsFromState(KeyguardState.AOD).map {
-                    1f - it.value
-                },
-                transitionInteractor.transitionStepsToState(KeyguardState.AOD).map { it.value }
-            ),
-        ) { startedKeyguardTransitionStep, aodTransitionAmount ->
-            if (
-                startedKeyguardTransitionStep.to == KeyguardState.AOD ||
-                    startedKeyguardTransitionStep.from == KeyguardState.AOD
-            ) {
-                aodTransitionAmount
-            } else {
-                // in case a new transition (ie: to occluded) cancels a transition to or from
-                // aod, then we want to make sure the doze amount is still updated to 0
-                0f
-            }
-        }
+    private val dozeAmount: Flow<Float> = transitionInteractor.transitionValue(KeyguardState.AOD)
     // Burn-in offsets that animate based on the transition amount to AOD
     private val animatedBurnInOffsets: Flow<BurnInOffsets> =
         combine(nonAnimatedBurnInOffsets, dozeAmount) { burnInOffsets, dozeAmount ->
@@ -141,54 +122,57 @@
             )
         }
 
-    val deviceEntryViewAlpha: Flow<Float> =
+    val deviceEntryViewAlpha: StateFlow<Float> =
         combine(
-            transitionAlpha,
-            alphaMultiplierFromShadeExpansion,
-        ) { alpha, alphaMultiplier ->
-            alpha * alphaMultiplier
-        }
+                transitionAlpha,
+                alphaMultiplierFromShadeExpansion,
+            ) { alpha, alphaMultiplier ->
+                alpha * alphaMultiplier
+            }
+            .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initialValue = 0f)
     val useBackgroundProtection: StateFlow<Boolean> = isUdfpsSupported
     val burnInOffsets: Flow<BurnInOffsets> =
-        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
-            if (udfpsEnrolled) {
-                combine(
-                    transitionInteractor.startedKeyguardTransitionStep.sample(
-                        shadeInteractor.isAnyFullyExpanded,
-                        ::Pair
-                    ),
-                    animatedBurnInOffsets,
-                    nonAnimatedBurnInOffsets,
-                ) {
-                    (startedTransitionStep, shadeExpanded),
-                    animatedBurnInOffsets,
-                    nonAnimatedBurnInOffsets ->
-                    if (startedTransitionStep.to == KeyguardState.AOD) {
-                        when (startedTransitionStep.from) {
-                            KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
-                            KeyguardState.LOCKSCREEN ->
-                                if (shadeExpanded) {
-                                    nonAnimatedBurnInOffsets
-                                } else {
-                                    animatedBurnInOffsets
-                                }
-                            else -> nonAnimatedBurnInOffsets
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled
+            .flatMapLatest { udfpsEnrolled ->
+                if (udfpsEnrolled) {
+                    combine(
+                        transitionInteractor.startedKeyguardTransitionStep.sample(
+                            shadeInteractor.isAnyFullyExpanded,
+                            ::Pair
+                        ),
+                        animatedBurnInOffsets,
+                        nonAnimatedBurnInOffsets,
+                    ) {
+                        (startedTransitionStep, shadeExpanded),
+                        animatedBurnInOffsets,
+                        nonAnimatedBurnInOffsets ->
+                        if (startedTransitionStep.to == KeyguardState.AOD) {
+                            when (startedTransitionStep.from) {
+                                KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
+                                KeyguardState.LOCKSCREEN ->
+                                    if (shadeExpanded) {
+                                        nonAnimatedBurnInOffsets
+                                    } else {
+                                        animatedBurnInOffsets
+                                    }
+                                else -> nonAnimatedBurnInOffsets
+                            }
+                        } else if (startedTransitionStep.from == KeyguardState.AOD) {
+                            when (startedTransitionStep.to) {
+                                KeyguardState.LOCKSCREEN -> animatedBurnInOffsets
+                                else -> BurnInOffsets(x = 0, y = 0, progress = 0f)
+                            }
+                        } else {
+                            BurnInOffsets(x = 0, y = 0, progress = 0f)
                         }
-                    } else if (startedTransitionStep.from == KeyguardState.AOD) {
-                        when (startedTransitionStep.to) {
-                            KeyguardState.LOCKSCREEN -> animatedBurnInOffsets
-                            else -> BurnInOffsets(x = 0, y = 0, progress = 0f)
-                        }
-                    } else {
-                        BurnInOffsets(x = 0, y = 0, progress = 0f)
                     }
+                } else {
+                    // If UDFPS isn't enrolled, we don't show any UI on AOD so there's no need
+                    // to use burn in offsets at all
+                    flowOf(BurnInOffsets(x = 0, y = 0, progress = 0f))
                 }
-            } else {
-                // If UDFPS isn't enrolled, we don't show any UI on AOD so there's no need
-                // to use burn in offsets at all
-                flowOf(BurnInOffsets(x = 0, y = 0, progress = 0f))
             }
-        }
+            .distinctUntilChanged()
 
     private val isUnlocked: Flow<Boolean> =
         keyguardInteractor.isKeyguardDismissible.flatMapLatest { isUnlocked ->
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index c997617..bf80e18 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -70,8 +70,7 @@
     // dispatcher to use. We don't want it to run on the Dispatchers.Default thread pool as
     // default behavior. Instead, we want it to run on the view's UI thread since the user will
     // presumably want to call view methods that require being called from said UI thread.
-    val lifecycleCoroutineContext =
-        Dispatchers.Main + createCoroutineTracingContext() + coroutineContext
+    val lifecycleCoroutineContext = MAIN_DISPATCHER_SINGLETON + coroutineContext
     val traceName =
         if (Compile.IS_DEBUG && coroutineTracing()) {
             inferTraceSectionName()
@@ -205,17 +204,28 @@
             StackWalker.getInstance().walk { stream ->
                 stream.filter(::isFrameInteresting).limit(5).findFirst()
             }
-        if (interestingFrame.isPresent) {
+        return if (interestingFrame.isPresent) {
             val f = interestingFrame.get()
-            return "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]"
+            "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]"
         } else {
-            return DEFAULT_TRACE_NAME
+            DEFAULT_TRACE_NAME
         }
     } finally {
         Trace.traceEnd(Trace.TRACE_TAG_APP)
     }
 }
 
+/**
+ * Even though there is only has one usage of `Dispatchers.Main` in this file, we cache it in a
+ * top-level property so that we do not unnecessarily create new `CoroutineContext` objects for
+ * tracing on each call to [repeatWhenAttached]. It is okay to reuse a single instance of the
+ * tracing context because it is copied for its children.
+ *
+ * Also, ideally, we would use the injected `@Main CoroutineDispatcher`, but [repeatWhenAttached] is
+ * an extension function, and plumbing dagger-injected instances for static usage has little
+ * benefit.
+ */
+private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + createCoroutineTracingContext()
 private const val DEFAULT_TRACE_NAME = "repeatWhenAttached"
 private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt"
 private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
index 93021ba..cfcea98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
@@ -91,7 +91,15 @@
             .queryIntentServicesAsUser(INTENT, FLAGS, userId)
             .mapNotNull { it.serviceInfo }
             .filter { it.permission == BIND_QUICK_SETTINGS_TILE }
-            .filter { packageManager.isComponentActuallyEnabled(it) }
+            .filter {
+                try {
+                    packageManager.isComponentActuallyEnabled(it)
+                } catch (e: IllegalArgumentException) {
+                    // If the package is not found, it means it was uninstalled between query
+                    // and now. So it's clearly not enabled.
+                    false
+                }
+            }
             .mapTo(mutableSetOf()) { it.componentName }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSLongPressProperties.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSLongPressProperties.kt
index a2ded6a..71cf9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSLongPressProperties.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSLongPressProperties.kt
@@ -22,8 +22,8 @@
  * These properties are used during animation if a tile supports a long-press action.
  */
 data class QSLongPressProperties(
-    var xScale: Float,
-    var yScale: Float,
+    var height: Float,
+    var width: Float,
     var cornerRadius: Float,
     var backgroundColor: Int,
     var labelColor: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 40cf4a4..ca5b771 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -25,6 +25,7 @@
 import android.content.res.Resources.ID_NULL
 import android.graphics.Color
 import android.graphics.PorterDuff
+import android.graphics.Rect
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.LayerDrawable
@@ -46,6 +47,7 @@
 import android.widget.Switch
 import android.widget.TextView
 import androidx.annotation.VisibleForTesting
+import androidx.core.graphics.drawable.updateBounds
 import com.android.app.tracing.traceSection
 import com.android.settingslib.Utils
 import com.android.systemui.Flags
@@ -62,7 +64,6 @@
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
 import com.android.systemui.res.R
-import com.android.systemui.util.children
 import kotlinx.coroutines.DisposableHandle
 import java.util.Objects
 
@@ -83,6 +84,10 @@
         const val UNAVAILABLE_ALPHA = 0.3f
         @VisibleForTesting
         internal const val TILE_STATE_RES_PREFIX = "tile_states_"
+        @VisibleForTesting
+        internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f
+        @VisibleForTesting
+        internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f
     }
 
     private val icon: QSIconViewImpl = QSIconViewImpl(context)
@@ -180,6 +185,8 @@
     private val locInScreen = IntArray(2)
 
     /** Visuo-haptic long-press effects */
+    private var haveLongPressPropertiesBeenReset = true
+    private var paddingForLaunch = Rect()
     private var initialLongPressProperties: QSLongPressProperties? = null
     private var finalLongPressProperties: QSLongPressProperties? = null
     private val colorEvaluator = ArgbEvaluator.getInstance()
@@ -326,7 +333,7 @@
     private fun updateHeight() {
         // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
         //  launch animation.
-        if (scaleX != 1f || scaleY != 1f) {
+        if (!haveLongPressPropertiesBeenReset && longPressEffect != null) {
             // The launch animation of a long-press effect did not reset the long-press effect so
             // we must do it here
             resetLongPressEffectProperties()
@@ -632,7 +639,7 @@
                     )
             }
             showRippleEffect = false
-            initializeLongPressProperties()
+            initializeLongPressProperties(measuredHeight, measuredWidth)
         } else {
             // Long-press effects might have been enabled before but the new state does not
             // handle a long-press. In this case, we go back to the behaviour of a regular tile
@@ -765,8 +772,60 @@
 
     override fun onActivityLaunchAnimationEnd() = resetLongPressEffectProperties()
 
+    fun prepareForLaunch() {
+        val startingHeight = initialLongPressProperties?.height?.toInt() ?: 0
+        val startingWidth = initialLongPressProperties?.width?.toInt() ?: 0
+        val deltaH = finalLongPressProperties?.height?.minus(startingHeight)?.toInt() ?: 0
+        val deltaW = finalLongPressProperties?.width?.minus(startingWidth)?.toInt() ?: 0
+        paddingForLaunch.left = -deltaW / 2
+        paddingForLaunch.top = -deltaH / 2
+        paddingForLaunch.right = deltaW / 2
+        paddingForLaunch.bottom = deltaH / 2
+    }
+
+    override fun getPaddingForLaunchAnimation(): Rect = paddingForLaunch
+
     fun updateLongPressEffectProperties(effectProgress: Float) {
         if (!isLongClickable || longPressEffect == null) return
+
+        if (haveLongPressPropertiesBeenReset) haveLongPressPropertiesBeenReset = false
+
+        // Dimensions change
+        val newHeight =
+            interpolateFloat(
+                effectProgress,
+                initialLongPressProperties?.height ?: 0f,
+                finalLongPressProperties?.height ?: 0f,
+            ).toInt()
+        val newWidth =
+            interpolateFloat(
+                effectProgress,
+                initialLongPressProperties?.width ?: 0f,
+                finalLongPressProperties?.width ?: 0f,
+            ).toInt()
+
+        val startingHeight = initialLongPressProperties?.height?.toInt() ?: 0
+        val startingWidth = initialLongPressProperties?.width?.toInt() ?: 0
+        val deltaH = (newHeight - startingHeight) / 2
+        val deltaW = (newWidth - startingWidth) / 2
+
+        background.updateBounds(
+            left = -deltaW,
+            top = -deltaH,
+            right = newWidth - deltaW,
+            bottom = newHeight - deltaH,
+        )
+
+        // Radius change
+        val newRadius =
+            interpolateFloat(
+                effectProgress,
+                initialLongPressProperties?.cornerRadius ?: 0f,
+                finalLongPressProperties?.cornerRadius ?: 0f,
+            )
+        changeCornerRadius(newRadius)
+
+        // Color change
         setAllColors(
             colorEvaluator.evaluate(
                 effectProgress,
@@ -802,32 +861,6 @@
                 finalLongPressProperties?.iconColor ?: 0,
             ) as Int,
         )
-
-        val newScaleX =
-            interpolateFloat(
-                effectProgress,
-                initialLongPressProperties?.xScale ?: 1f,
-                finalLongPressProperties?.xScale ?: 1f,
-            )
-        val newScaleY =
-            interpolateFloat(
-                effectProgress,
-                initialLongPressProperties?.xScale ?: 1f,
-                finalLongPressProperties?.xScale ?: 1f,
-            )
-        val newRadius =
-            interpolateFloat(
-                effectProgress,
-                initialLongPressProperties?.cornerRadius ?: 0f,
-                finalLongPressProperties?.cornerRadius ?: 0f,
-            )
-        scaleX = newScaleX
-        scaleY = newScaleY
-        for (child in children) {
-            child.scaleX = 1f / newScaleX
-            child.scaleY = 1f / newScaleY
-        }
-        changeCornerRadius(newRadius)
     }
 
     private fun unbindLongPressEffect() {
@@ -839,12 +872,12 @@
         start + fraction * (end - start)
 
     fun resetLongPressEffectProperties() {
-        scaleY = 1f
-        scaleX = 1f
-        for (child in children) {
-            child.scaleY = 1f
-            child.scaleX = 1f
-        }
+        background.updateBounds(
+            left = 0,
+            top = 0,
+            right = initialLongPressProperties?.width?.toInt() ?: 0,
+            bottom = initialLongPressProperties?.height?.toInt() ?: 0,
+        )
         changeCornerRadius(resources.getDimensionPixelSize(R.dimen.qs_corner_radius).toFloat())
         setAllColors(
             getBackgroundColorForState(lastState, lastDisabledByPolicy),
@@ -854,13 +887,15 @@
             getOverlayColorForState(lastState),
         )
         icon.setTint(icon.mIcon as ImageView, lastIconTint)
+        haveLongPressPropertiesBeenReset = true
     }
 
-    private fun initializeLongPressProperties() {
+    @VisibleForTesting
+    fun initializeLongPressProperties(startingHeight: Int, startingWidth: Int) {
         initialLongPressProperties =
             QSLongPressProperties(
-                /* xScale= */1f,
-                /* yScale= */1f,
+                height = startingHeight.toFloat(),
+                width = startingWidth.toFloat(),
                 resources.getDimensionPixelSize(R.dimen.qs_corner_radius).toFloat(),
                 getBackgroundColorForState(lastState),
                 getLabelColorForState(lastState),
@@ -872,8 +907,8 @@
 
         finalLongPressProperties =
             QSLongPressProperties(
-                /* xScale= */1.1f,
-                /* yScale= */1.2f,
+                height = LONG_PRESS_EFFECT_HEIGHT_SCALE * startingHeight,
+                width = LONG_PRESS_EFFECT_WIDTH_SCALE * startingWidth,
                 resources.getDimensionPixelSize(R.dimen.qs_corner_radius).toFloat() - 20,
                 getBackgroundColorForState(Tile.STATE_ACTIVE),
                 getLabelColorForState(Tile.STATE_ACTIVE),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index ff5fdc6..851bfca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -229,7 +229,9 @@
         // the gesture area doesn't overlap with widgets.
         // TODO(b/323035776): adjust gesture areaa for portrait mode
         containerView.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
+            // Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and not
+            // occluded.
+            lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) {
                 val exclusionRect =
                     Rect(
                         0,
@@ -242,12 +244,19 @@
             }
         }
 
+        // Listen to bouncer visibility directly as these flows become true as soon as any portion
+        // of the bouncers are visible when the transition starts. The keyguard transition state
+        // only changes once transitions are fully finished, which would mean touches during a
+        // transition to the bouncer would be incorrectly intercepted by the hub.
         collectFlow(
             containerView,
-            keyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState),
+            or(
+                keyguardInteractor.primaryBouncerShowing,
+                keyguardInteractor.alternateBouncerShowing
+            ),
             {
                 anyBouncerShowing = it
-                updateLifecycleState()
+                updateTouchHandlingState()
             }
         )
         collectFlow(
@@ -255,7 +264,7 @@
             communalInteractor.isCommunalShowing,
             {
                 hubShowing = it
-                updateLifecycleState()
+                updateTouchHandlingState()
             }
         )
         collectFlow(
@@ -263,7 +272,7 @@
             and(shadeInteractor.isAnyFullyExpanded, not(shadeInteractor.isUserInteracting)),
             {
                 shadeShowing = it
-                updateLifecycleState()
+                updateTouchHandlingState()
             }
         )
         collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
@@ -276,13 +285,22 @@
     /**
      * Updates the lifecycle stored by the [lifecycleRegistry] to control when the [touchMonitor]
      * should listen for and intercept top and bottom swipes.
+     *
+     * Also clears gesture exclusion zones when the hub is occluded or gone.
      */
-    private fun updateLifecycleState() {
+    private fun updateTouchHandlingState() {
         val shouldInterceptGestures = hubShowing && !(shadeShowing || anyBouncerShowing)
         if (shouldInterceptGestures) {
             lifecycleRegistry.currentState = Lifecycle.State.RESUMED
         } else {
+            // Hub is either occluded or no longer showing, turn off touch handling.
             lifecycleRegistry.currentState = Lifecycle.State.STARTED
+
+            // Clear exclusion rects if the hub is not showing or is covered, so we don't interfere
+            // with back gestures when the bouncer or shade. We do this here instead of with
+            // repeatOnLifecycle as repeatOnLifecycle does not run when going from RESUMED back to
+            // STARTED, only when going from CREATED to STARTED.
+            communalContainerView!!.systemGestureExclusionRects = emptyList()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 4d7dacd..7c1101b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -382,16 +382,16 @@
 
     private void clearCurrentMediaNotificationSession() {
         mMediaMetadata = null;
-        if (mMediaController != null) {
-            if (DEBUG_MEDIA) {
-                Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
-                        + mMediaController.getPackageName());
-            }
-            mBackgroundExecutor.execute(() -> {
+        mBackgroundExecutor.execute(() -> {
+            if (mMediaController != null) {
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
+                            + mMediaController.getPackageName());
+                }
                 mMediaController.unregisterCallback(mMediaListener);
                 mMediaController = null;
-            });
-        }
+            }
+        });
     }
 
     public interface MediaListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 2f9c2f0..6909907 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -82,7 +82,7 @@
         launch { viewModel.stackBottom.collect { view.setStackBottom(it) } }
         launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
         launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
-        launch { viewModel.expandFraction.collect { view.setExpandFraction(it) } }
+        launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
         launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
         launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 054116d..51c053e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -84,14 +84,15 @@
         if (Flags.oemEnabledSatelliteFlag()) {
                 iconsInteractor.icons.aggregateOver(
                     selector = { intr ->
-                        combine(intr.isInService, intr.isEmergencyOnly) {
+                        combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
                             isInService,
-                            isEmergencyOnly ->
-                            !isInService && !isEmergencyOnly
+                            isEmergencyOnly,
+                            isNtn ->
+                            !isInService && !(isEmergencyOnly || isNtn)
                         }
                     }
-                ) { isOosAndIsNotEmergencyOnly ->
-                    isOosAndIsNotEmergencyOnly.all { it }
+                ) { isOosAndNotEmergencyOnlyOrSatellite ->
+                    isOosAndNotEmergencyOnlyOrSatellite.all { it }
                 }
             } else {
                 flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index ed44699..19d9c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.filterData
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
@@ -69,6 +70,7 @@
                     }
                 } else {
                     mediaOutputInteractor.defaultActiveMediaSession
+                        .filterData()
                         .flatMapLatest {
                             localMediaRepositoryFactory
                                 .create(it?.packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
deleted file mode 100644
index bac7d15..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.mediaoutput.domain
-
-import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
-import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-
-/** Determines if the Media Output Volume Panel component is available. */
-@VolumePanelScope
-class MediaOutputAvailabilityCriteria
-@Inject
-constructor(
-    private val audioModeInteractor: AudioModeInteractor,
-) : ComponentAvailabilityCriteria {
-
-    override fun isAvailable(): Flow<Boolean> = audioModeInteractor.isOngoingCall.map { !it }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 83b8029..b974f90 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -28,6 +28,9 @@
 import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.Result
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.android.systemui.volume.panel.shared.model.wrapInResult
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
@@ -72,7 +75,7 @@
         }
 
     /** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */
-    val defaultActiveMediaSession: StateFlow<MediaDeviceSession?> =
+    val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> =
         activeMediaControllers
             .map {
                 when {
@@ -82,11 +85,13 @@
                     else -> null
                 }
             }
+            .wrapInResult()
             .flowOn(backgroundCoroutineContext)
-            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
 
     private val localMediaRepository: SharedFlow<LocalMediaRepository> =
         defaultActiveMediaSession
+            .filterData()
             .map { it?.packageName }
             .distinctUntilChanged()
             .map { localMediaRepositoryFactory.create(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index d60d981..192e0ec 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -18,16 +18,21 @@
 
 import android.content.Context
 import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Color
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.res.R
+import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.model.AudioOutputDevice
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import com.android.systemui.volume.panel.shared.model.Result
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.android.systemui.volume.panel.shared.model.wrapInResult
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -35,6 +40,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.mapNotNull
@@ -50,25 +56,25 @@
     @VolumePanelScope private val coroutineScope: CoroutineScope,
     private val actionsInteractor: MediaOutputActionsInteractor,
     private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+    audioOutputInteractor: AudioOutputInteractor,
+    audioModeInteractor: AudioModeInteractor,
     interactor: MediaOutputInteractor,
     private val uiEventLogger: UiEventLogger,
 ) {
 
     private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
         interactor.defaultActiveMediaSession
+            .filterData()
             .flatMapLatest { session ->
                 if (session == null) {
-                    flowOf(Result.Data<SessionWithPlaybackState?>(null))
+                    flowOf(null)
                 } else {
                     mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
-                        playback?.let {
-                            Result.Data<SessionWithPlaybackState?>(
-                                SessionWithPlaybackState(session, playback.isActive())
-                            )
-                        }
+                        playback?.let { SessionWithPlaybackState(session, playback.isActive) }
                     }
                 }
             }
+            .wrapInResult()
             .stateIn(
                 coroutineScope,
                 SharingStarted.Eagerly,
@@ -76,23 +82,24 @@
             )
 
     val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> =
-        combine(sessionWithPlaybackState, interactor.currentConnectedDevice) {
-                mediaDeviceSession,
-                currentConnectedDevice ->
-                if (mediaDeviceSession !is Result.Data) {
-                    return@combine null
-                }
-                ConnectedDeviceViewModel(
-                    if (mediaDeviceSession.data?.isPlaybackActive == true) {
-                        context.getString(
-                            R.string.media_output_label_title,
-                            mediaDeviceSession.data.session.appLabel
-                        )
-                    } else {
-                        context.getString(R.string.media_output_title_without_playing)
-                    },
-                    currentConnectedDevice?.name,
-                )
+        combine(
+                sessionWithPlaybackState.filterData(),
+                audioModeInteractor.isOngoingCall,
+                audioOutputInteractor.currentAudioDevice.filter {
+                    it !is AudioOutputDevice.Unknown
+                },
+            ) { mediaDeviceSession, isOngoingCall, currentConnectedDevice ->
+                val label =
+                    when {
+                        isOngoingCall -> context.getString(R.string.media_output_title_ongoing_call)
+                        mediaDeviceSession?.isPlaybackActive == true ->
+                            context.getString(
+                                R.string.media_output_label_title,
+                                mediaDeviceSession.session.appLabel
+                            )
+                        else -> context.getString(R.string.media_output_title_without_playing)
+                    }
+                ConnectedDeviceViewModel(label, currentConnectedDevice.name)
             }
             .stateIn(
                 coroutineScope,
@@ -101,16 +108,16 @@
             )
 
     val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
-        combine(sessionWithPlaybackState, interactor.currentConnectedDevice) {
+        combine(sessionWithPlaybackState.filterData(), audioOutputInteractor.currentAudioDevice) {
                 mediaDeviceSession,
                 currentConnectedDevice ->
-                if (mediaDeviceSession !is Result.Data) {
-                    return@combine null
-                }
                 val icon: Icon =
-                    currentConnectedDevice?.icon?.let { Icon.Loaded(it, null) }
+                    currentConnectedDevice
+                        .takeIf { currentConnectedDevice !is AudioOutputDevice.Unknown }
+                        ?.icon
+                        ?.let { Icon.Loaded(it, null) }
                         ?: Icon.Resource(R.drawable.ic_media_home_devices, null)
-                if (mediaDeviceSession.data?.isPlaybackActive == true) {
+                if (mediaDeviceSession?.isPlaybackActive == true) {
                     DeviceIconViewModel.IsPlaying(
                         icon = icon,
                         iconColor =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
index ac8092c..fa40059 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.isTheSameSession
 import com.android.systemui.volume.panel.component.volume.domain.model.SliderType
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.filterData
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.coroutineScope
@@ -46,7 +47,7 @@
     val volumePanelSliders: StateFlow<List<SliderType>> =
         combineTransform(
                 mediaOutputInteractor.activeMediaDeviceSessions,
-                mediaOutputInteractor.defaultActiveMediaSession,
+                mediaOutputInteractor.defaultActiveMediaSession.filterData(),
                 audioRepository.communicationDevice,
             ) { activeSessions, defaultSession, communicationDevice ->
                 coroutineScope {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
index 741f5cf..26d6a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.CastVolumeSliderViewModel
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.filterData
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -80,9 +81,13 @@
     val isExpanded: StateFlow<Boolean> =
         merge(
                 mutableIsExpanded,
-                mediaOutputInteractor.defaultActiveMediaSession.flatMapLatest {
-                    if (it == null) flowOf(true)
-                    else mediaDeviceSessionInteractor.playbackState(it).map { it?.isActive != true }
+                mediaOutputInteractor.defaultActiveMediaSession.filterData().flatMapLatest { session
+                    ->
+                    if (session == null) flowOf(true)
+                    else
+                        mediaDeviceSessionInteractor.playbackState(session).map {
+                            it?.isActive != true
+                        }
                 },
             )
             .stateIn(scope, SharingStarted.Eagerly, false)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt
index 8793538..5daed99 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/Result.kt
@@ -16,6 +16,10 @@
 
 package com.android.systemui.volume.panel.shared.model
 
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+
 /** Models a loadable result */
 sealed interface Result<T> {
 
@@ -25,3 +29,9 @@
     /** The data is loaded successfully */
     data class Data<T>(val data: T) : Result<T>
 }
+
+/** Wraps flow into [Result]. */
+fun <T> Flow<T>.wrapInResult(): Flow<Result<T>> = map { Result.Data(it) }
+
+/** Filters only [Result.Data] from the flow. */
+fun <T> Flow<Result<T>>.filterData(): Flow<T> = mapNotNull { it as? Result.Data<T> }.map { it.data }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index ecbd0f5..b5ef8c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tileimpl
 
 import android.content.Context
+import android.graphics.Rect
 import android.graphics.drawable.Drawable
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
@@ -474,6 +475,31 @@
         assertThat(tileView.isLongPressEffectInitialized).isFalse()
     }
 
+    @Test
+    fun onPrepareForLaunch_paddingForLaunchAnimationIsConfigured() {
+        val startingWidth = 100
+        val startingHeight = 50
+        val deltaWidth = (QSTileViewImpl.LONG_PRESS_EFFECT_WIDTH_SCALE - 1f) * startingWidth
+        val deltaHeight = (QSTileViewImpl.LONG_PRESS_EFFECT_HEIGHT_SCALE - 1f) * startingHeight
+
+        // GIVEN that long-press effect properties are initialized
+        tileView.initializeLongPressProperties(startingHeight, startingWidth)
+
+        // WHEN the tile is preparing for the launch animation
+        tileView.prepareForLaunch()
+
+        // THE animation padding corresponds to the tile's growth due to the effect
+        val padding = tileView.getPaddingForLaunchAnimation()
+        assertThat(padding).isEqualTo(
+            Rect(
+                -deltaWidth.toInt() / 2,
+                -deltaHeight.toInt() / 2,
+                deltaWidth.toInt() / 2,
+                deltaHeight.toInt() / 2,
+            )
+        )
+    }
+
     class FakeTileView(
         context: Context,
         collapsed: Boolean,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 19b137c..99204e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.ambient.touch.TouchHandler
 import com.android.systemui.ambient.touch.TouchMonitor
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.fakeCommunalRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
@@ -42,10 +43,8 @@
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -238,11 +237,7 @@
                 goToScene(CommunalScenes.Communal)
 
                 // Bouncer is visible.
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    KeyguardState.GLANCEABLE_HUB,
-                    KeyguardState.PRIMARY_BOUNCER,
-                    testScope
-                )
+                fakeKeyguardBouncerRepository.setPrimaryShow(true)
                 testableLooper.processAllMessages()
 
                 // Touch events are not intercepted.
@@ -368,11 +363,7 @@
                 goToScene(CommunalScenes.Communal)
 
                 // Bouncer is visible.
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    KeyguardState.GLANCEABLE_HUB,
-                    KeyguardState.PRIMARY_BOUNCER,
-                    testScope
-                )
+                fakeKeyguardBouncerRepository.setPrimaryShow(true)
                 testableLooper.processAllMessages()
 
                 assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
@@ -387,11 +378,7 @@
                 goToScene(CommunalScenes.Communal)
 
                 // Bouncer is visible.
-                fakeKeyguardTransitionRepository.sendTransitionSteps(
-                    KeyguardState.GLANCEABLE_HUB,
-                    KeyguardState.ALTERNATE_BOUNCER,
-                    testScope
-                )
+                fakeKeyguardBouncerRepository.setAlternateVisible(true)
                 testableLooper.processAllMessages()
 
                 assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
@@ -452,6 +439,53 @@
             }
         }
 
+    @Test
+    fun gestureExclusionZone_unsetWhenShadeOpen() =
+        with(kosmos) {
+            testScope.runTest {
+                goToScene(CommunalScenes.Communal)
+
+                // Shade shows up.
+                shadeTestUtil.setQsExpansion(1.0f)
+                testableLooper.processAllMessages()
+
+                // Exclusion rects are unset.
+                assertThat(containerView.systemGestureExclusionRects).isEmpty()
+            }
+        }
+
+    @Test
+    fun gestureExclusionZone_unsetWhenBouncerOpen() =
+        with(kosmos) {
+            testScope.runTest {
+                goToScene(CommunalScenes.Communal)
+
+                // Bouncer is visible.
+                fakeKeyguardBouncerRepository.setPrimaryShow(true)
+                testableLooper.processAllMessages()
+
+                // Exclusion rects are unset.
+                assertThat(containerView.systemGestureExclusionRects).isEmpty()
+            }
+        }
+
+    @Test
+    fun gestureExclusionZone_unsetWhenHubClosed() =
+        with(kosmos) {
+            testScope.runTest {
+                goToScene(CommunalScenes.Communal)
+
+                // Exclusion rect is set.
+                assertThat(containerView.systemGestureExclusionRects).hasSize(1)
+
+                // Leave the hub.
+                goToScene(CommunalScenes.Blank)
+
+                // Exclusion rect is unset.
+                assertThat(containerView.systemGestureExclusionRects).isEmpty()
+            }
+        }
+
     private fun initAndAttachContainerView() {
         containerView = View(context)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index c4ab943..405e3ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -250,7 +250,7 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_twoConnectionsOos_yes() =
+    fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_yes() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
@@ -258,11 +258,13 @@
             val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
             val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
 
-            // WHEN all of the connections are OOS
+            // WHEN all of the connections are OOS and none are NTN
             i1.isInService.value = false
             i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
             i2.isInService.value = false
             i2.isEmergencyOnly.value = false
+            i2.isNonTerrestrial.value = false
 
             // THEN the value is propagated to this interactor
             assertThat(latest).isTrue()
@@ -270,7 +272,31 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_oneConnectionOos_yes() =
+    fun areAllConnectionsOutOfService_twoConnectionsOos_oneNtn_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 2 connections
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+            // WHEN all of the connections are OOS and one is NTN
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
+            i2.isInService.value = false
+            i2.isEmergencyOnly.value = false
+
+            // sub2 is non terrestrial, consider it connected for the sake of the iconography
+            i2.isNonTerrestrial.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_yes() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
@@ -280,6 +306,7 @@
             // WHEN all of the connections are OOS
             i1.isInService.value = false
             i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
 
             // THEN the value is propagated to this interactor
             assertThat(latest).isTrue()
@@ -287,7 +314,25 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_oneConnectionInService_no() =
+    fun areAllConnectionsOutOfService_oneConnectionOos_ntn_yes() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+
+            // WHEN all of the connections are OOS
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_oneConnectionInService_nonNtn_no() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
@@ -296,6 +341,7 @@
 
             // WHEN all of the connections are NOT OOS
             i1.isInService.value = true
+            i1.isNonTerrestrial.value = false
 
             // THEN the value is propagated to this interactor
             assertThat(latest).isFalse()
@@ -303,7 +349,24 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_twoConnectionsOneInService_no() =
+    fun areAllConnectionsOutOfService_oneConnectionInService_ntn_no() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+            // GIVEN, 1 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+
+            // WHEN all of the connections are NOT OOS
+            i1.isInService.value = true
+            i1.isNonTerrestrial.value = true
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun areAllConnectionsOutOfService_twoConnectionsOneInService_nonNtn_no() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
@@ -313,7 +376,9 @@
 
             // WHEN at least 1 connection is NOT OOS.
             i1.isInService.value = false
+            i1.isNonTerrestrial.value = false
             i2.isInService.value = true
+            i2.isNonTerrestrial.value = false
 
             // THEN the value is propagated to this interactor
             assertThat(latest).isFalse()
@@ -321,7 +386,7 @@
 
     @Test
     @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    fun areAllConnectionsOutOfService_twoConnectionsInService_no() =
+    fun areAllConnectionsOutOfService_twoConnectionsInService_nonNtn_no() =
         testScope.runTest {
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
 
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 3337419..e06f400 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -155,6 +155,31 @@
     visibility: ["//frameworks/base"],
 }
 
+cc_library_shared {
+    name: "libravenwood_runtime",
+    host_supported: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+    ],
+
+    srcs: [
+        "runtime-helper-src/jni/*.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libnativehelper",
+        "libutils",
+        "libcutils",
+    ],
+    visibility: ["//frameworks/base"],
+}
+
 // For collecting the *stats.csv files in a known directory under out/host/linux-x86/testcases/.
 // The "test" just shows the available stats filenames.
 sh_test_host {
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
index 7dc197e..7a3142b 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
@@ -28,8 +28,10 @@
  * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"})
  * of a callback to get a callback at the class load time.
  *
- * The method must be {@code public static} with a single argument that takes
- * {@link Class}.
+ * The method must be {@code public static} with a single argument that takes {@link Class}.
+ *
+ * Typically, this is used with {@link #LIBANDROID_LOADING_HOOK}, which will load the necessary
+ * native libraries.
  *
  * @hide
  */
@@ -37,4 +39,10 @@
 @Retention(RetentionPolicy.CLASS)
 public @interface RavenwoodClassLoadHook {
     String value();
+
+    /**
+     * Class load hook that loads <code>libandroid_runtime</code>.
+     */
+    public static String LIBANDROID_LOADING_HOOK
+            = "com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded";
 }
diff --git a/ravenwood/bivalenttest/Android.bp b/ravenwood/bivalenttest/Android.bp
index a6b6ed9..2d94894 100644
--- a/ravenwood/bivalenttest/Android.bp
+++ b/ravenwood/bivalenttest/Android.bp
@@ -45,7 +45,6 @@
     jni_libs: [
         "libravenwoodbivalenttest_jni",
     ],
-    sdk_version: "test_current",
     auto_gen_config: true,
 }
 
diff --git a/ravenwood/bivalenttest/AndroidTest.xml b/ravenwood/bivalenttest/AndroidTest.xml
index ac4695b..9e5dd11 100644
--- a/ravenwood/bivalenttest/AndroidTest.xml
+++ b/ravenwood/bivalenttest/AndroidTest.xml
@@ -25,5 +25,8 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.ravenwood.bivalenttest" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+
+        <!-- Need to use MODULE_LIBRARIES APIs, so no hidden API check. -->
+        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp b/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp
index 83f756e..956d79c 100644
--- a/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp
+++ b/ravenwood/bivalenttest/jni/ravenwood_core_test_jni.cpp
@@ -15,19 +15,70 @@
  */
 
 #include <nativehelper/JNIHelp.h>
+#include <atomic>
 #include "jni.h"
 #include "utils/Log.h"
 #include "utils/misc.h"
 
+// JNI methods for RavenwoodJniTest
+
 static jint add(JNIEnv* env, jclass clazz, jint a, jint b) {
     return a + b;
 }
 
-static const JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods_JniTest[] =
 {
     { "add", "(II)I", (void*)add },
 };
 
+// JNI methods for RavenwoodNativeAllocationRegistryTest
+std::atomic<int> numTotalAlloc = 0;
+
+class NarTestData {
+public:
+    NarTestData(jint v): value(v) {
+        numTotalAlloc++;
+    }
+
+    ~NarTestData() {
+        value = -1;
+        numTotalAlloc--;
+    }
+
+    volatile jint value;
+};
+
+static jlong NarTestData_nMalloc(JNIEnv* env, jclass clazz, jint value) {
+    NarTestData* p = new NarTestData(value);
+    return reinterpret_cast<jlong>(p);
+}
+
+static jint NarTestData_nGet(JNIEnv* env, jclass clazz, jlong ptr) {
+    NarTestData* p = reinterpret_cast<NarTestData*>(ptr);
+    return p->value;
+}
+
+static void NarTestData_free(jlong ptr) {
+    NarTestData* p = reinterpret_cast<NarTestData*>(ptr);
+    delete p;
+}
+
+static jlong NarTestData_nGetNativeFinalizer(JNIEnv* env, jclass clazz) {
+    return reinterpret_cast<jlong>(NarTestData_free);
+}
+
+static jint NarTestData_nGetTotalAlloc(JNIEnv* env, jclass clazz) {
+    return numTotalAlloc;
+}
+
+static const JNINativeMethod sMethods_NarTestData[] =
+{
+    { "nMalloc", "(I)J", (void*)NarTestData_nMalloc },
+    { "nGet", "(J)I", (void*)NarTestData_nGet },
+    { "nGetNativeFinalizer", "()J", (void*)NarTestData_nGetNativeFinalizer },
+    { "nGetTotalAlloc", "()I", (void*)NarTestData_nGetTotalAlloc },
+};
+
 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
 {
     JNIEnv* env = NULL;
@@ -43,7 +94,13 @@
 
     int res = jniRegisterNativeMethods(env,
             "com/android/ravenwoodtest/bivalenttest/RavenwoodJniTest",
-            sMethods, NELEM(sMethods));
+            sMethods_JniTest, NELEM(sMethods_JniTest));
+    if (res < 0) {
+        return res;
+    }
+    res = jniRegisterNativeMethods(env,
+            "com/android/ravenwoodtest/bivalenttest/RavenwoodNativeAllocationRegistryTest$Data",
+            sMethods_NarTestData, NELEM(sMethods_NarTestData));
     if (res < 0) {
         return res;
     }
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodJniTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodJniTest.java
index 59467e9..0cc2adc 100644
--- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodJniTest.java
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodJniTest.java
@@ -29,6 +29,10 @@
 @RunWith(AndroidJUnit4.class)
 public final class RavenwoodJniTest {
     static {
+        initializeJni();
+    }
+
+    public static void initializeJni() {
         RavenwoodUtils.loadJniLibrary("ravenwoodbivalenttest_jni");
     }
 
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodNativeAllocationRegistryTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodNativeAllocationRegistryTest.java
new file mode 100644
index 0000000..415b467
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodNativeAllocationRegistryTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.bivalenttest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import libcore.util.NativeAllocationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodNativeAllocationRegistryTest {
+    private static final String TAG = RavenwoodNativeAllocationRegistryTest.class.getSimpleName();
+    static {
+        RavenwoodJniTest.initializeJni();
+    }
+
+    @Rule
+    public final RavenwoodRule mRavenwoodRule = new RavenwoodRule();
+
+    private static class Data {
+        private final long mNativePtr;
+
+        private static native long nMalloc(int value);
+        private static native int nGet(long ptr);
+        private static native long nGetNativeFinalizer();
+
+        public static native int nGetTotalAlloc();
+
+        public int get() {
+            return nGet(mNativePtr);
+        }
+
+        private static class NarHolder {
+            public static final NativeAllocationRegistry sRegistry =
+                    NativeAllocationRegistry.createMalloced(
+                            Data.class.getClassLoader(), nGetNativeFinalizer());
+        }
+
+        public Data(int value) {
+            mNativePtr = nMalloc(value);
+            NarHolder.sRegistry.registerNativeAllocation(this, mNativePtr);
+        }
+    }
+
+    @Test
+    public void testNativeAllocationRegistry() {
+
+        final long timeoutTime = mRavenwoodRule.realCurrentTimeMillis() + 10_000;
+
+        final int startAlloc = Data.nGetTotalAlloc();
+
+        int totalAlloc = 0;
+
+        // Keep allocation new objects, until some get released.
+
+        while (true) {
+            for (int i = 0; i < 1000; i++) {
+                totalAlloc++;
+                Data d = new Data(i);
+                assertEquals(i, d.get());
+            }
+            System.gc();
+
+            final int currentAlloc = Data.nGetTotalAlloc() - startAlloc;
+            Log.i(TAG, "# of currently allocated objects=" + currentAlloc);
+
+            if (currentAlloc < totalAlloc) {
+                break; // Good, some objects have been released;
+            }
+            if (mRavenwoodRule.realCurrentTimeMillis() > timeoutTime) {
+                fail("No objects have been released before timeout");
+            }
+        }
+    }
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 21d8019..9d12f85 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -426,4 +426,15 @@
             return ENABLE_OPTIONAL_VALIDATION;
         }
     }
+
+    /**
+     * Returns the "real" result from {@link System#currentTimeMillis()}.
+     *
+     * Currently, it's the same thing as calling {@link System#currentTimeMillis()},
+     * but this one is guaranteeed to return the real value, even when Ravenwood supports
+     * injecting a time to{@link System#currentTimeMillis()}.
+     */
+    public long realCurrentTimeMillis() {
+        return System.currentTimeMillis();
+    }
 }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
index 37ceac6..99ab327 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
@@ -26,6 +26,15 @@
     private RavenwoodUtils() {
     }
 
+    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+
+    // LibcoreRavenwoodUtils calls it with reflections.
+    public static void loadRavenwoodNativeRuntime() {
+        if (RavenwoodRule.isOnRavenwood()) {
+            RavenwoodUtils.loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME);
+        }
+    }
+
     /**
      * Load a JNI library respecting {@code java.library.path}
      * (which reflects {@code LD_LIBRARY_PATH}).
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index e951351b..773a89a 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -39,4 +39,8 @@
     public static void validate(Statement base, Description description,
             boolean enableOptionalValidation) {
     }
+
+    public static long realCurrentTimeMillis() {
+        return System.currentTimeMillis();
+    }
 }
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 9057d16..96b7057 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -44,6 +44,14 @@
 
     public static final String LIBANDROID_RUNTIME_NAME = "android_runtime";
 
+    /**
+     * Extra strings needed to pass to register_android_graphics_classes().
+     *
+     * `android.graphics.Graphics` is not actually a class, so we can't use the same initialization
+     * strategy than the "normal" classes. So we just hardcode it here.
+     */
+    public static final String GRAPHICS_EXTRA_INIT_PARAMS = ",android.graphics.Graphics";
+
     private static String sInitialDir = new File("").getAbsolutePath();
 
     static {
@@ -98,7 +106,6 @@
     private static void loadFrameworkNativeCode() {
         // This is called from class-initializers, so no synchronization is needed.
         if (sLoadFrameworkNativeCodeCalled) {
-            // This method has already been called before.s
             return;
         }
         sLoadFrameworkNativeCodeCalled = true;
@@ -112,7 +119,8 @@
         }
 
         if (SKIP_LOADING_LIBANDROID) {
-            log("Skip loading " + LIBANDROID_RUNTIME_NAME);
+            log("Skip loading native runtime.");
+            return;
         }
 
         // Make sure these properties are not set.
@@ -121,27 +129,39 @@
         ensurePropertyNotSet(KEYBOARD_PATHS);
         ensurePropertyNotSet(GRAPHICS_NATIVE_CLASSES);
 
-        // Tell libandroid what JNI to use.
-        final var jniClasses = getCoreNativeClassesToUse();
-        if (jniClasses.isEmpty()) {
-            log("No classes require JNI methods, skip loading " + LIBANDROID_RUNTIME_NAME);
+        // Load the libraries, if needed.
+        final var libanrdoidClasses = getClassesWithNativeMethods(sLibandroidClasses);
+        final var libhwuiClasses = getClassesWithNativeMethods(sLibhwuiClasses);
+        if (libanrdoidClasses.isEmpty() && libhwuiClasses.isEmpty()) {
+            log("No classes require JNI methods, skip loading native runtime.");
             return;
         }
-        setProperty(CORE_NATIVE_CLASSES, jniClasses);
-        setProperty(GRAPHICS_NATIVE_CLASSES, "");
+        setProperty(CORE_NATIVE_CLASSES, libanrdoidClasses);
+        setProperty(GRAPHICS_NATIVE_CLASSES, libhwuiClasses + GRAPHICS_EXTRA_INIT_PARAMS);
 
+        log("Loading " + LIBANDROID_RUNTIME_NAME + " for '" + libanrdoidClasses + "' and '"
+                + libhwuiClasses + "'");
         RavenwoodUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
     }
 
     /**
-     * Classes with native methods that are backed by `libandroid_runtime`.
+     * Classes with native methods that are backed by libandroid_runtime.
      *
-     * At runtime, we check if these classes have any methods, and if so, we'll have
-     * `libandroid_runtime` register the native functions.
+     * See frameworks/base/core/jni/platform/host/HostRuntime.cpp
      */
-    private static final Class<?>[] sClassesWithLibandroidNativeMethods = {
+    private static final Class<?>[] sLibandroidClasses = {
             android.util.Log.class,
-            android.os.Parcel.class,
+    };
+
+    /**
+     * Classes with native methods that are backed by libhwui.
+     *
+     * See frameworks/base/libs/hwui/apex/LayoutlibLoader.cpp
+     */
+    private static final Class<?>[] sLibhwuiClasses = {
+            android.graphics.Interpolator.class,
+            android.graphics.Matrix.class,
+            android.graphics.Path.class,
     };
 
     /**
@@ -157,17 +177,15 @@
     }
 
     /**
-     * Create a list of classes as comma-separated that require JNI methods to be set up.
-     *
-     * <p>This list is used by frameworks/base/core/jni/LayoutlibLoader.cpp to decide
-     * what JNI methods to set up.
+     * Create a list of classes as comma-separated that require JNI methods to be set up from
+     * a given class list, ignoring classes with no native methods.
      */
-    private static String getCoreNativeClassesToUse() {
+    private static String getClassesWithNativeMethods(Class<?>[] classes) {
         final var coreNativeClassesToLoad = new ArrayList<String>();
 
-        for (var clazz : sClassesWithLibandroidNativeMethods) {
+        for (var clazz : classes) {
             if (hasNativeMethod(clazz)) {
-                log("Class %s has native methods", clazz);
+                log("Class %s has native methods", clazz.getCanonicalName());
                 coreNativeClassesToLoad.add(clazz.getName());
             }
         }
diff --git a/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp b/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
new file mode 100644
index 0000000..8e3a21d
--- /dev/null
+++ b/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+
+typedef void (*FreeFunction)(void*);
+
+static void NativeAllocationRegistry_applyFreeFunction(JNIEnv*,
+                                                       jclass,
+                                                       jlong freeFunction,
+                                                       jlong ptr) {
+    void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+    FreeFunction nativeFreeFunction
+        = reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+    nativeFreeFunction(nativePtr);
+}
+
+static const JNINativeMethod sMethods_NAR[] =
+{
+    { "applyFreeFunction", "(JJ)V", (void*)NativeAllocationRegistry_applyFreeFunction },
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+
+    ALOGI("%s: JNI_OnLoad", __FILE__);
+
+    // Initialize the Ravenwood version of NativeAllocationRegistry.
+    // We don't use this JNI on the device side, but if we ever have to do, skip this part.
+#ifndef __ANDROID__
+    int res = jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry",
+            sMethods_NAR, NELEM(sMethods_NAR));
+    if (res < 0) {
+        return res;
+    }
+#endif
+
+    return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
new file mode 100644
index 0000000..839b62a
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.ravenwood;
+
+public class LibcoreRavenwoodUtils {
+    private LibcoreRavenwoodUtils() {
+    }
+
+    public static void loadRavenwoodNativeRuntime() {
+        // TODO Stop using reflections.
+        // We need to call RavenwoodUtils.loadRavenwoodNativeRuntime(), but due to the build
+        // structure complexity, we can't refer to to this method directly from here,
+        // so let's use reflections for now...
+        try {
+            final var clazz = Class.forName("android.platform.test.ravenwood.RavenwoodUtils");
+            final var method = clazz.getMethod("loadRavenwoodNativeRuntime");
+            method.invoke(null);
+        } catch (Throwable th) {
+            throw new IllegalStateException("Failed to load Ravenwood native runtime", th);
+        }
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
new file mode 100644
index 0000000..93861e8
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.util;
+
+import libcore.ravenwood.LibcoreRavenwoodUtils;
+
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+
+/**
+ * Re-implementation of ART's NativeAllocationRegistry for Ravenwood.
+ * - We don't track the native allocation size on Ravenwood.
+ * - sun.misc.Cleaner isn't available on the desktop JVM, so we use java.lang.ref.Cleaner.
+ *   (Should ART switch to java.lang.ref.Cleaner?)
+ */
+public class NativeAllocationRegistry {
+    static {
+        // Initialize the JNI method.
+        LibcoreRavenwoodUtils.loadRavenwoodNativeRuntime();
+    }
+
+    private final long mFreeFunction;
+    private static final Cleaner sCleaner = Cleaner.create();
+
+    public static NativeAllocationRegistry createNonmalloced(
+            ClassLoader classLoader, long freeFunction, long size) {
+        return new NativeAllocationRegistry(classLoader, freeFunction, size, false);
+    }
+
+    public static NativeAllocationRegistry createMalloced(
+            ClassLoader classLoader, long freeFunction, long size) {
+        return new NativeAllocationRegistry(classLoader, freeFunction, size, true);
+    }
+
+    public static NativeAllocationRegistry createMalloced(
+            ClassLoader classLoader, long freeFunction) {
+        return new NativeAllocationRegistry(classLoader, freeFunction, 0, true);
+    }
+
+    public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) {
+        this(classLoader, freeFunction, size, size == 0);
+    }
+
+    private NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size,
+            boolean mallocAllocation) {
+        if (size < 0) {
+            throw new IllegalArgumentException("Invalid native allocation size: " + size);
+        }
+        mFreeFunction = freeFunction;
+    }
+
+    public Runnable registerNativeAllocation(Object referent, long nativePtr) {
+        if (referent == null) {
+            throw new IllegalArgumentException("referent is null");
+        }
+        if (nativePtr == 0) {
+            throw new IllegalArgumentException("nativePtr is null");
+        }
+
+        final Runnable releaser = () -> {
+            applyFreeFunction(mFreeFunction, nativePtr);
+        };
+        sCleaner.register(referent, releaser);
+
+        // Ensure that cleaner doesn't get invoked before we enable it.
+        Reference.reachabilityFence(referent);
+        return releaser;
+    }
+
+    /**
+     * Calls {@code freeFunction}({@code nativePtr}).
+     */
+    public static native void applyFreeFunction(long freeFunction, long nativePtr);
+}
+
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 243e224..e452299 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -236,7 +236,11 @@
 
 android.accounts.Account
 
+android.graphics.Bitmap$Config
 android.graphics.Insets
+android.graphics.Interpolator
+android.graphics.Matrix
+android.graphics.Path
 android.graphics.Point
 android.graphics.PointF
 android.graphics.Rect
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ad869a1..2bece6c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -852,7 +852,7 @@
     }
 
     private void registerBroadcastReceivers() {
-        mPackageMonitor = new PackageMonitor() {
+        mPackageMonitor = new PackageMonitor(true) {
             @Override
             public void onSomePackagesChanged() {
                 if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionBackupHelper.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionBackupHelper.java
index d494be5..e91de37 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionBackupHelper.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionBackupHelper.java
@@ -123,8 +123,7 @@
      * Returns the system-gender to be backed up as a data-blob.
      */
     public byte[] getSystemBackupPayload(int userId) {
-        int gender = mGrammaticalGenderService.getSystemGrammaticalGender(mAttributionSource,
-                userId);
+        int gender = mGrammaticalGenderService.getSystemGrammaticalGender(userId);
         return intToByteArray(gender);
     }
 
@@ -167,7 +166,7 @@
         BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY);
     }
 
-    private byte[] convertToByteArray(HashMap<String, Integer> pkgGenderInfo) {
+    private static byte[] convertToByteArray(HashMap<String, Integer> pkgGenderInfo) {
         try (final ByteArrayOutputStream out = new ByteArrayOutputStream();
              final ObjectOutputStream objStream = new ObjectOutputStream(out)) {
             objStream.writeObject(pkgGenderInfo);
@@ -178,22 +177,22 @@
         }
     }
 
-    private byte[] intToByteArray(final int gender) {
+    private static byte[] intToByteArray(final int gender) {
         ByteBuffer bb = ByteBuffer.allocate(4);
         bb.putInt(gender);
         return bb.array();
     }
 
-    private int convertByteArrayToInt(byte[] intBytes) {
+    private static int convertByteArrayToInt(byte[] intBytes) {
         ByteBuffer byteBuffer = ByteBuffer.wrap(intBytes);
         return byteBuffer.getInt();
     }
 
-    private HashMap<String, Integer> readFromByteArray(byte[] payload) {
+    private static HashMap<String, Integer> readFromByteArray(byte[] payload) {
         HashMap<String, Integer> data = new HashMap<>();
 
-        try (ByteArrayInputStream byteIn = new ByteArrayInputStream(payload);
-             ObjectInputStream in = new ObjectInputStream(byteIn)) {
+        try (var byteIn = new ByteArrayInputStream(payload);
+                var in = new ObjectInputStream(byteIn)) {
             data = (HashMap<String, Integer>) in.readObject();
         } catch (IOException | ClassNotFoundException e) {
             Log.e(TAG, "cannot convert payload to HashMap.", e);
@@ -205,10 +204,10 @@
     private void cleanStagedDataForOldEntries() {
         for (int i = 0; i < mCache.size(); i++) {
             int userId = mCache.keyAt(i);
-            StagedData stagedData = mCache.get(userId);
+            StagedData stagedData = mCache.valueAt(userId);
             if (stagedData.mCreationTimeMillis
                     < mClock.millis() - STAGE_DATA_RETENTION_PERIOD.toMillis()) {
-                mCache.remove(userId);
+                mCache.removeAt(i--);
             }
         }
     }
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
index 2816d08..7eb971c 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
@@ -16,7 +16,6 @@
 
 package com.android.server.grammaticalinflection;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 
@@ -41,7 +40,7 @@
     public abstract void stageAndApplyRestoredPayload(byte[] payload, int userId);
 
     /**
-     * Get the current system grammatical gender of privileged application.
+     * Get the current system grammatical gender for the particular user.
      *
      * @return the value of grammatical gender
      *
@@ -50,18 +49,25 @@
     public abstract @Configuration.GrammaticalGender int getSystemGrammaticalGender(int userId);
 
     /**
-     * Retrieve the system grammatical gender.
+     * Get the final merged value of the global grammatical gender, user- or devsettings-set.
      *
      * @return the value of grammatical gender
      *
      */
-    public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender(
-            @NonNull Configuration configuration);
+    public abstract @Configuration.GrammaticalGender int mergedFinalSystemGrammaticalGender();
+
+    /**
+     * Get the grammatical gender from developer settings global override.
+     *
+     * @return the value of grammatical gender
+     */
+    public abstract
+            @Configuration.GrammaticalGender int getGrammaticalGenderFromDeveloperSettings();
 
     /**
      * Whether the package can get the system grammatical gender or not.
      */
-    public abstract boolean canGetSystemGrammaticalGender(int uid, @Nullable String packageName);
+    public abstract boolean canGetSystemGrammaticalGender(int uid);
 
 
     /**
@@ -74,4 +80,3 @@
      */
     public abstract void applyRestoredSystemPayload(byte[] payload, int userId);
 }
-
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 93a71b9..e242164 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -17,7 +17,6 @@
 package com.android.server.grammaticalinflection;
 
 import static android.app.Flags.systemTermsOfAddressEnabled;
-import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
 
 import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
 
@@ -43,6 +42,7 @@
 import android.util.SparseIntArray;
 import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
@@ -126,7 +126,7 @@
 
         @Override
         public void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
-            isCallerAllowed();
+            enforceCallerPermissions();
             GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
                     userId);
         }
@@ -138,18 +138,16 @@
                         + " does not have READ_SYSTEM_GRAMMATICAL_GENDER permission.");
             }
             return checkSystemTermsOfAddressIsEnabled()
-                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
-                    attributionSource, userId)
-                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(userId)
+                    : Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
         @Override
         public int peekSystemGrammaticalGenderByUserId(AttributionSource attributionSource,
                 int userId) {
             return canGetSystemGrammaticalGender(attributionSource)
-                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
-                    attributionSource, userId)
-                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(userId)
+                    : Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
         @Override
@@ -163,11 +161,10 @@
 
     private final class GrammaticalInflectionManagerInternalImpl
             extends GrammaticalInflectionManagerInternal {
-
         @Override
         @Nullable
         public byte[] getBackupPayload(int userId) {
-            isCallerAllowed();
+            enforceCallerPermissions();
             return mBackupHelper.getBackupPayload(userId);
         }
 
@@ -179,7 +176,7 @@
         @Override
         @Nullable
         public byte[] getSystemBackupPayload(int userId) {
-            isCallerAllowed();
+            enforceCallerPermissions();
             return mBackupHelper.getSystemBackupPayload(userId);
         }
 
@@ -191,30 +188,35 @@
         @Override
         public int getSystemGrammaticalGender(int userId) {
             return checkSystemTermsOfAddressIsEnabled()
-                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
-                    mContext.getAttributionSource(), userId)
-                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(userId)
+                    : Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
         @Override
-        public int retrieveSystemGrammaticalGender(Configuration configuration) {
+        public int mergedFinalSystemGrammaticalGender() {
             int systemGrammaticalGender = getSystemGrammaticalGender(mContext.getUserId());
             // Retrieve the grammatical gender from system property, set it into
             // configuration which will get updated later if the grammatical gender raw value of
             // current configuration is {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
-            if (configuration.getGrammaticalGenderRaw()
-                    == Configuration.GRAMMATICAL_GENDER_UNDEFINED
-                    || systemGrammaticalGender <= Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) {
-                systemGrammaticalGender = SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
-                        Configuration.GRAMMATICAL_GENDER_UNDEFINED);
+            if (systemGrammaticalGender == Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+                systemGrammaticalGender = getGrammaticalGenderFromDeveloperSettings();
             }
-            return systemGrammaticalGender;
+            return systemGrammaticalGender == Configuration.GRAMMATICAL_GENDER_UNDEFINED
+                    ? Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED : systemGrammaticalGender;
         }
 
         @Override
-        public boolean canGetSystemGrammaticalGender(int uid, String packageName) {
-            AttributionSource attributionSource = new AttributionSource.Builder(
-                    uid).setPackageName(packageName).build();
+        public int getGrammaticalGenderFromDeveloperSettings() {
+            return SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
+                    Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED);
+        }
+
+        @Override
+        public boolean canGetSystemGrammaticalGender(int uid) {
+            if (uid == Process.SYSTEM_UID) {
+                return true;
+            }
+            var attributionSource = new AttributionSource.Builder(uid).build();
             return GrammaticalInflectionService.this.canGetSystemGrammaticalGender(
                     attributionSource);
         }
@@ -225,7 +227,7 @@
                 mActivityTaskManagerInternal.getApplicationConfig(appPackageName, userId);
 
         if (appConfig == null || appConfig.mGrammaticalGender == null) {
-            return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+            return Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
         } else {
             return appConfig.mGrammaticalGender;
         }
@@ -239,9 +241,10 @@
                         userId);
 
         if (!SystemProperties.getBoolean(GRAMMATICAL_INFLECTION_ENABLED, true)) {
-            if (preValue != GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+            if (preValue != Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) {
                 Log.d(TAG, "Clearing the user's grammatical gender setting");
-                updater.setGrammaticalGender(GRAMMATICAL_GENDER_NOT_SPECIFIED).commit();
+                updater.setGrammaticalGender(
+                        Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED).commit();
             }
             return;
         }
@@ -250,49 +253,48 @@
         FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_GRAMMATICAL_INFLECTION_CHANGED,
                 FrameworkStatsLog.APPLICATION_GRAMMATICAL_INFLECTION_CHANGED__SOURCE_ID__OTHERS,
                 uid,
-                gender != GRAMMATICAL_GENDER_NOT_SPECIFIED,
-                preValue != GRAMMATICAL_GENDER_NOT_SPECIFIED);
+                gender != Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
+                preValue != Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED);
 
         updater.setGrammaticalGender(gender).commit();
     }
 
     protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
-        Trace.beginSection("GrammaticalInflectionService.setSystemWideGrammaticalGender");
-        if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
-                grammaticalGender)) {
-            throw new IllegalArgumentException("Unknown grammatical gender");
-        }
-
-        if (!checkSystemTermsOfAddressIsEnabled()) {
-            if (grammaticalGender == GRAMMATICAL_GENDER_NOT_SPECIFIED) {
-                return;
+        try {
+            if (!checkSystemTermsOfAddressIsEnabled()) {
+                return; // Nothing to do, and the flag can't get flipped at the runtime.
             }
-            Log.d(TAG, "Clearing the system grammatical gender setting");
-            grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
-        }
 
-        synchronized (mLock) {
+            Trace.beginSection("GrammaticalInflectionService.setSystemWideGrammaticalGender");
+            if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+                    grammaticalGender)) {
+                throw new IllegalArgumentException("Unknown grammatical gender");
+            }
+
             final File file = getGrammaticalGenderFile(userId);
-            final AtomicFile atomicFile = new AtomicFile(file);
-            FileOutputStream stream = null;
-            try {
-                stream = atomicFile.startWrite();
-                stream.write(toXmlByteArray(grammaticalGender, stream));
-                atomicFile.finishWrite(stream);
-                mGrammaticalGenderCache.put(userId, grammaticalGender);
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to write file " + atomicFile, e);
-                if (stream != null) {
-                    atomicFile.failWrite(stream);
+            synchronized (mLock) {
+                final AtomicFile atomicFile = new AtomicFile(file);
+                FileOutputStream stream = null;
+                try {
+                    stream = atomicFile.startWrite();
+                    stream.write(toXmlByteArray(grammaticalGender, stream));
+                    atomicFile.finishWrite(stream);
+                    mGrammaticalGenderCache.put(userId, grammaticalGender);
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to write file " + atomicFile, e);
+                    if (stream != null) {
+                        atomicFile.failWrite(stream);
+                    }
+                    throw new RuntimeException(e);
                 }
-                throw new RuntimeException(e);
             }
+            updateConfiguration(grammaticalGender, userId);
+        } finally {
+            Trace.endSection();
         }
-        updateConfiguration(grammaticalGender, userId);
-        Trace.endSection();
     }
 
-    private void updateConfiguration(int grammaticalGender, int userId) {
+    private static void updateConfiguration(int grammaticalGender, int userId) {
         try {
             Configuration config = new Configuration();
             int preValue = config.getGrammaticalGender();
@@ -301,54 +303,47 @@
             FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_GRAMMATICAL_INFLECTION_CHANGED,
                     FrameworkStatsLog.SYSTEM_GRAMMATICAL_INFLECTION_CHANGED__SOURCE_ID__SYSTEM,
                     userId,
-                    grammaticalGender != GRAMMATICAL_GENDER_NOT_SPECIFIED,
-                    preValue != GRAMMATICAL_GENDER_NOT_SPECIFIED);
+                    grammaticalGender != Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
+                    preValue != Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED);
             GrammaticalInflectionBackupHelper.notifyBackupManager();
         } catch (RemoteException e) {
             Log.w(TAG, "Can not update configuration", e);
         }
     }
 
-    public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
-        String packageName = attributionSource.getPackageName();
-        if (packageName == null) {
-            Log.d(TAG, "Package name is null.");
-            return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-        }
-
+    /**
+     * Returns the system global grammatical gender value for the requested user.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+    public int getSystemGrammaticalGender(int userId) {
         synchronized (mLock) {
             int grammaticalGender = mGrammaticalGenderCache.get(userId);
-            return grammaticalGender < 0 ? GRAMMATICAL_GENDER_NOT_SPECIFIED : grammaticalGender;
+            return grammaticalGender < 0
+                    ? Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED : grammaticalGender;
         }
     }
 
-    private File getGrammaticalGenderFile(int userId) {
+    private static File getGrammaticalGenderFile(int userId) {
         final File dir = new File(Environment.getDataSystemCeDirectory(userId),
                 TAG_GRAMMATICAL_INFLECTION);
         return new File(dir, USER_SETTINGS_FILE_NAME);
     }
 
-    private byte[] toXmlByteArray(int grammaticalGender, FileOutputStream fileStream) {
-
-        try {
-            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-            TypedXmlSerializer out = Xml.resolveSerializer(fileStream);
-            out.setOutput(outputStream, StandardCharsets.UTF_8.name());
-            out.startDocument(/* encoding= */ null, /* standalone= */ true);
-            out.startTag(null, TAG_GRAMMATICAL_INFLECTION);
-            out.attributeInt(null, ATTR_NAME, grammaticalGender);
-            out.endTag(null, TAG_GRAMMATICAL_INFLECTION);
-            out.endDocument();
-
-            return outputStream.toByteArray();
-        } catch (IOException e) {
-            return null;
-        }
+    private static byte[] toXmlByteArray(int grammaticalGender, FileOutputStream fileStream)
+            throws IOException {
+        var outputStream = new ByteArrayOutputStream();
+        TypedXmlSerializer out = Xml.resolveSerializer(fileStream);
+        out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+        out.startDocument(/* encoding= */ null, /* standalone= */ true);
+        out.startTag(null, TAG_GRAMMATICAL_INFLECTION);
+        out.attributeInt(null, ATTR_NAME, grammaticalGender);
+        out.endTag(null, TAG_GRAMMATICAL_INFLECTION);
+        out.endDocument();
+        return outputStream.toByteArray();
     }
 
-    private int getGrammaticalGenderFromXml(TypedXmlPullParser parser)
+    private static int getGrammaticalGenderFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
-
         XmlUtils.nextElement(parser);
         while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
             String tagName = parser.getName();
@@ -359,20 +354,20 @@
             }
         }
 
-        return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+        return Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
     }
 
-    private void isCallerAllowed() {
+    private void enforceCallerPermissions() {
         int callingUid = Binder.getCallingUid();
         if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID
                 && callingUid != Process.ROOT_UID) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.CHANGE_CONFIGURATION,
-                    "Caller must be system, shell, root or has CHANGE_CONFIGURATION permission.");
+                    "Caller must be system, shell, root or hold CHANGE_CONFIGURATION permission.");
         }
     }
 
-    private boolean checkSystemTermsOfAddressIsEnabled() {
+    private static boolean checkSystemTermsOfAddressIsEnabled() {
         if (!systemTermsOfAddressEnabled()) {
             Log.d(TAG, "The flag must be enabled to allow calling the API.");
             return false;
@@ -387,25 +382,31 @@
 
     @Override
     public void onUserUnlocked(TargetUser user) {
+        if (!checkSystemTermsOfAddressIsEnabled()) {
+            return;
+        }
         IoThread.getHandler().post(() -> {
-            int userId = user.getUserIdentifier();
+            final int userId = user.getUserIdentifier();
             final File file = getGrammaticalGenderFile(userId);
+            final int grammaticalGender;
             synchronized (mLock) {
                 if (!file.exists()) {
                     Log.d(TAG, "User " + userId + " doesn't have the grammatical gender file.");
                     return;
                 }
-                if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
-                    try (FileInputStream in = new FileInputStream(file)) {
-                        final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-                        int grammaticalGender = getGrammaticalGenderFromXml(parser);
-                        mGrammaticalGenderCache.put(userId, grammaticalGender);
-                        updateConfiguration(grammaticalGender, userId);
-                    } catch (IOException | XmlPullParserException e) {
-                        Log.e(TAG, "Failed to parse XML configuration from " + file, e);
-                    }
+                if (mGrammaticalGenderCache.indexOfKey(userId) >= 0) {
+                    return;
+                }
+                try (FileInputStream in = new FileInputStream(file)) {
+                    final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                    grammaticalGender = getGrammaticalGenderFromXml(parser);
+                    mGrammaticalGenderCache.put(userId, grammaticalGender);
+                } catch (IOException | XmlPullParserException e) {
+                    Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+                    return;
                 }
             }
+            updateConfiguration(grammaticalGender, userId);
         });
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 54e1217..d10e192 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -867,6 +867,60 @@
                         }
                     }
                 }, mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        reportFeatures(true);
+                    }
+                },
+                mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        reportFeatures(false);
+                    }
+                },
+                mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        reportFeatures(false);
+                    }
+                },
+                mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        reportFeatures(false);
+                    }
+                },
+                mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        reportFeatures(false);
+                    }
+                },
+                mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        reportFeatures(false);
+                    }
+                },
+                mServiceThreadExecutor);
 
         if (isTvDevice()) {
             mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
@@ -968,6 +1022,21 @@
         }
     }
 
+    /** Helper method for sending feature discovery command */
+    private void reportFeatures(boolean isTvDeviceSetting) {
+        // check if tv device is enabled for tv device specific RC profile setting
+        if (isTvDeviceSetting) {
+            if (isTvDeviceEnabled()) {
+                tv().reportFeatures();
+            }
+        } else { // check for source device setting
+            HdmiCecLocalDeviceSource source = isAudioSystemDevice() ? audioSystem() : playback();
+            if (source != null) {
+                source.reportFeatures();
+            }
+        }
+    }
+
     /**
      * Returns the initial power status used when the HdmiControlService starts.
      */
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index ef52d2a..e28939b 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -986,7 +986,7 @@
     }
 
     private void startTrackingPackageChanges() {
-        final PackageMonitor monitor = new PackageMonitor() {
+        final PackageMonitor monitor = new PackageMonitor(true) {
 
             @Override
             public void onPackageUpdateStarted(@NonNull String packageName, int uid) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 44a200e..1a83177 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -471,11 +471,6 @@
         return mBindingController.getSelectedMethodId();
     }
 
-    @GuardedBy("ImfLock.class")
-    private void setSelectedMethodIdLocked(@Nullable String selectedMethodId) {
-        mBindingController.setSelectedMethodId(selectedMethodId);
-    }
-
     /**
      * The current binding sequence number, incremented every time there is
      * a new bind performed.
@@ -934,6 +929,10 @@
          */
         private ArrayList<String> mDataClearedPackages = new ArrayList<>();
 
+        private MyPackageMonitor() {
+            super(true);
+        }
+
         @GuardedBy("ImfLock.class")
         void clearKnownImePackageNamesLocked() {
             mKnownImePackageNames.clear();
@@ -2497,7 +2496,7 @@
 
     @GuardedBy("ImfLock.class")
     void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
-        setSelectedMethodIdLocked(null);
+        mBindingController.setSelectedMethodId(null);
         // Callback before clean-up binding states.
         onUnbindCurrentMethodByReset();
         mBindingController.unbindCurrentMethod();
@@ -3077,7 +3076,7 @@
             // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
             // because mCurMethodId is stored as a history in
             // setSelectedInputMethodAndSubtypeLocked().
-            setSelectedMethodIdLocked(id);
+            mBindingController.setSelectedMethodId(id);
 
             if (mActivityManagerInternal.isSystemReady()) {
                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -3401,7 +3400,14 @@
         mBindingController.setCurrentMethodVisible();
         final IInputMethodInvoker curMethod = getCurMethodLocked();
         ImeTracker.forLogging().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
-        if (curMethod != null) {
+        final boolean readyToDispatchToIme;
+        if (Flags.deferShowSoftInputUntilSessionCreation()) {
+            readyToDispatchToIme =
+                    curMethod != null && mCurClient != null && mCurClient.mCurSession != null;
+        } else {
+            readyToDispatchToIme = curMethod != null;
+        }
+        if (readyToDispatchToIme) {
             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
             mCurStatsToken = null;
 
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 2c14532..c5e2bb8 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -291,9 +291,15 @@
 
     private void setOldSettingForBackworkCompatibility(boolean isActive) {
         // Set the SECURE_FRP_MODE flag, for backward compatibility with clients who use it.
-        // They should switch to calling #isFrpActive().
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.SECURE_FRP_MODE, isActive ? 1 : 0);
+        // They should switch to calling #isFrpActive().  Clear calling ID since this can happen
+        // during an app call.
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.SECURE_FRP_MODE, isActive ? 1 : 0);
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
     }
 
     private void setOemUnlockEnabledProperty(boolean oemUnlockEnabled) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 306f77d..3862b79 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -734,11 +734,11 @@
                     .addCategory(Intent.CATEGORY_BROWSABLE)
                     .build();
 
-    /** SMS and MMS can be handled by the private profile or by the parent user. */
+    /** SMS and MMS are always handled in the main user. */
     private static final DefaultCrossProfileIntentFilter SMS_MMS_PRIVATE_PROFILE =
             new DefaultCrossProfileIntentFilter.Builder(
                     DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
-                    ONLY_IF_NO_MATCH_FOUND,
+                    SKIP_CURRENT_PROFILE,
                     /* letsPersonalDataIntoProfile= */ false)
                     .addAction(Intent.ACTION_VIEW)
                     .addAction(Intent.ACTION_SENDTO)
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 3f5ec06..82902d4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -50,7 +50,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.IShortcutService;
 import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.ShortcutChangeCallback;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -152,7 +151,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
@@ -322,11 +320,12 @@
 
     private final Handler mHandler;
 
-    private final CopyOnWriteArrayList<ShortcutChangeListener> mListeners =
-            new CopyOnWriteArrayList<>();
+    @GuardedBy("mServiceLock")
+    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
 
-    private final CopyOnWriteArrayList<ShortcutChangeCallback> mShortcutChangeCallbacks =
-            new CopyOnWriteArrayList<>();
+    @GuardedBy("mServiceLock")
+    private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
+            new ArrayList<>(1);
 
     private final AtomicLong mRawLastResetTime = new AtomicLong(0);
 
@@ -1842,11 +1841,18 @@
             @UserIdInt final int userId) {
         return () -> {
             try {
-                if (!isUserUnlockedL(userId)) {
-                    return;
+                final ArrayList<ShortcutChangeListener> copy;
+                synchronized (mServiceLock) {
+                    if (!isUserUnlockedL(userId)) {
+                        return;
+                    }
+
+                    copy = new ArrayList<>(mListeners);
                 }
                 // Note onShortcutChanged() needs to be called with the system service permissions.
-                mListeners.forEach(listener -> listener.onShortcutChanged(packageName, userId));
+                for (int i = copy.size() - 1; i >= 0; i--) {
+                    copy.get(i).onShortcutChanged(packageName, userId);
+                }
             } catch (Exception ignore) {
             }
         };
@@ -1861,17 +1867,22 @@
         final UserHandle user = UserHandle.of(userId);
         injectPostToHandler(() -> {
             try {
-                if (!isUserUnlockedL(userId)) {
-                    return;
+                final ArrayList<LauncherApps.ShortcutChangeCallback> copy;
+                synchronized (mServiceLock) {
+                    if (!isUserUnlockedL(userId)) {
+                        return;
+                    }
+
+                    copy = new ArrayList<>(mShortcutChangeCallbacks);
                 }
-                mShortcutChangeCallbacks.forEach(callback -> {
+                for (int i = copy.size() - 1; i >= 0; i--) {
                     if (!CollectionUtils.isEmpty(changedList)) {
-                        callback.onShortcutsAddedOrUpdated(packageName, changedList, user);
+                        copy.get(i).onShortcutsAddedOrUpdated(packageName, changedList, user);
                     }
                     if (!CollectionUtils.isEmpty(removedList)) {
-                        callback.onShortcutsRemoved(packageName, removedList, user);
+                        copy.get(i).onShortcutsRemoved(packageName, removedList, user);
                     }
-                });
+                }
             } catch (Exception ignore) {
             }
         });
@@ -3414,13 +3425,17 @@
 
         @Override
         public void addListener(@NonNull ShortcutChangeListener listener) {
-            mListeners.add(Objects.requireNonNull(listener));
+            synchronized (mServiceLock) {
+                mListeners.add(Objects.requireNonNull(listener));
+            }
         }
 
         @Override
         public void addShortcutChangeCallback(
                 @NonNull LauncherApps.ShortcutChangeCallback callback) {
-            mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
+            synchronized (mServiceLock) {
+                mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index 7126cb5..7c2ce64 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -110,7 +110,7 @@
         final EnabledComponentsObserver o = new EnabledComponentsObserver(context, settingName,
                 servicePermission, serviceName, lock, listeners);
 
-        PackageMonitor packageMonitor = new PackageMonitor() {
+        PackageMonitor packageMonitor = new PackageMonitor(true) {
             @Override
             public void onSomePackagesChanged() {
                 o.onPackagesChanged();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f1ba755..d20b3b2 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1346,6 +1346,10 @@
     }
 
     class MyPackageMonitor extends PackageMonitor {
+        private MyPackageMonitor() {
+            super(true);
+        }
+
         @Override
         public void onPackageUpdateFinished(String packageName, int uid) {
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7d057a9..3503516 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9492,6 +9492,12 @@
         return false;
     }
 
+    @Override
+    protected boolean setOverrideGender(Configuration requestsTmpConfig, int gender) {
+        return WindowProcessController.applyConfigGenderOverride(
+                requestsTmpConfig, gender, mAtmService.mGrammaticalManagerInternal, getUid());
+    }
+
     @VisibleForTesting
     @Override
     Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index fc05d17..f3e1dfb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -949,7 +949,7 @@
         }
 
         configuration.setGrammaticalGender(
-                mGrammaticalManagerInternal.retrieveSystemGrammaticalGender(configuration));
+                mGrammaticalManagerInternal.mergedFinalSystemGrammaticalGender());
 
         synchronized (mGlobalLock) {
             mForceResizableActivities = forceResizable;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 31754bf..efd5202 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -534,8 +534,8 @@
                 nightMode);
         boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig,
                 locales);
-        boolean newGenderSet = (gender != null) && setOverrideGender(mRequestsTmpConfig,
-                gender);
+        boolean newGenderSet = setOverrideGender(mRequestsTmpConfig,
+                gender == null ? Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED : gender);
         if (newNightModeSet || newLocalesSet || newGenderSet) {
             onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
         }
@@ -577,14 +577,11 @@
      *
      * @return true if the grammatical gender has been changed.
      */
-    private boolean setOverrideGender(Configuration requestsTmpConfig,
+    protected boolean setOverrideGender(Configuration requestsTmpConfig,
             @Configuration.GrammaticalGender int gender) {
-        if (mRequestedOverrideConfiguration.getGrammaticalGender() == gender) {
-            return false;
-        } else {
-            requestsTmpConfig.setGrammaticalGender(gender);
-            return true;
-        }
+        // Noop, only ActivityRecord and WindowProcessController have enough knowledge about the
+        // app to apply gender correctly.
+        return false;
     }
 
     public boolean isActivityTypeDream() {
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 23127ac..9d597ea 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -169,6 +169,8 @@
                         LocaleOverlayHelper.combineLocalesIfOverlayExists(
                         modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales()),
                         modifiedRecord.mGrammaticalGender);
+            } else {
+                container.applyAppSpecificConfig(null, null, null);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 301d0a5..56e5d76 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -47,7 +47,6 @@
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
 import static android.view.SurfaceControl.METADATA_TASK_ID;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -170,12 +169,10 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.RemoteAnimationAdapter;
 import android.view.Surface;
 import android.view.SurfaceControl;
-import android.view.TaskTransitionSpec;
 import android.view.WindowManager;
 import android.view.WindowManager.TransitionOldType;
 import android.window.ITaskOrganizer;
@@ -2939,33 +2936,6 @@
         return;
     }
 
-    /**
-     * Account for specified insets to crop the animation bounds by to avoid the animation
-     * occurring over "out of bounds" regions
-     *
-     * For example this is used to make sure the tasks are cropped to be fully above the expanded
-     * taskbar when animating.
-     *
-     * TEMPORARY FIELD (b/202383002)
-     * TODO: Remove once we use surfaceflinger rounded corners on tasks rather than taskbar overlays
-     *       or when shell transitions are fully enabled
-     *
-     * @param animationBounds The animation bounds to adjust to account for the custom spec insets.
-     */
-    void adjustAnimationBoundsForTransition(Rect animationBounds) {
-        TaskTransitionSpec spec = mWmService.mTaskTransitionSpec;
-        if (spec != null) {
-            final InsetsState state =
-                    getDisplayContent().getInsetsStateController().getRawInsetsState();
-            for (int i = state.sourceSize() - 1; i >= 0; i--) {
-                final InsetsSource source = state.sourceAt(i);
-                if (source.hasFlags(FLAG_INSETS_ROUNDED_CORNER)) {
-                    animationBounds.inset(source.calculateVisibleInsets(animationBounds));
-                }
-            }
-        }
-    }
-
     void setDragResizing(boolean dragResizing) {
         if (mDragResizing != dragResizing) {
             // No need to check if allowed if it's leaving dragResize
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index cb8304c..d70ca02 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -44,7 +44,6 @@
 import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
 import static com.android.server.wm.AppTransition.isActivityTransitOld;
 import static com.android.server.wm.AppTransition.isTaskFragmentTransitOld;
-import static com.android.server.wm.AppTransition.isTaskTransitOld;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
@@ -72,7 +71,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.res.Configuration;
@@ -103,7 +101,6 @@
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceControlViewHost;
 import android.view.SurfaceSession;
-import android.view.TaskTransitionSpec;
 import android.view.WindowManager;
 import android.view.WindowManager.TransitionOldType;
 import android.view.animation.Animation;
@@ -3146,9 +3143,6 @@
         // Separate position and size for use in animators.
         final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
         mTmpRect.set(screenBounds);
-        if (this.asTask() != null && isTaskTransitOld(transit)) {
-            this.asTask().adjustAnimationBoundsForTransition(mTmpRect);
-        }
         getAnimationPosition(mTmpPoint);
         mTmpRect.offsetTo(0, 0);
 
@@ -3283,10 +3277,6 @@
 
             AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder();
 
-            if (isTaskTransitOld(transit) && getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-                animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor());
-            }
-
             // Check if the animation requests to show background color for Activity and embedded
             // TaskFragment.
             final ActivityRecord activityRecord = asActivityRecord();
@@ -3340,18 +3330,6 @@
         }
     }
 
-    private @ColorInt int getTaskAnimationBackgroundColor() {
-        Context uiContext = mDisplayContent.getDisplayPolicy().getSystemUiContext();
-        TaskTransitionSpec customSpec = mWmService.mTaskTransitionSpec;
-        @ColorInt int defaultFallbackColor = uiContext.getColor(R.color.overview_background);
-
-        if (customSpec != null && customSpec.backgroundColor != 0) {
-            return customSpec.backgroundColor;
-        }
-
-        return defaultFallbackColor;
-    }
-
     final SurfaceAnimationRunner getSurfaceAnimationRunner() {
         return mWmService.mSurfaceAnimationRunner;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b19f0be..feede01 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -20,7 +20,6 @@
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INPUT_CONSUMER;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
-import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.Manifest.permission.MANAGE_APP_TOKENS;
 import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
@@ -295,7 +294,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.SurfaceSession;
-import android.view.TaskTransitionSpec;
 import android.view.View;
 import android.view.View.FocusDirection;
 import android.view.ViewDebug;
@@ -763,11 +761,6 @@
      */
     final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());
 
-    /**
-     * Used during task transitions to allow SysUI and launcher to customize task transitions.
-     */
-    TaskTransitionSpec mTaskTransitionSpec;
-
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
 
@@ -9932,24 +9925,6 @@
     }
 
     @Override
-    public void setTaskTransitionSpec(TaskTransitionSpec spec) {
-        if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS, "setTaskTransitionSpec()")) {
-            throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
-        }
-
-        mTaskTransitionSpec = spec;
-    }
-
-    @Override
-    public void clearTaskTransitionSpec() {
-        if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS, "clearTaskTransitionSpec()")) {
-            throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
-        }
-
-        mTaskTransitionSpec = null;
-    }
-
-    @Override
     @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
     public void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
             ITaskFpsCallback callback) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index fd1b5be..1c00fbb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -84,6 +84,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.Watchdog;
+import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
 import com.android.server.wm.ActivityTaskManagerService.HotPath;
 
 import java.io.IOException;
@@ -324,8 +325,6 @@
      */
     private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
 
-    private final boolean mCanUseSystemGrammaticalGender;
-
     public WindowProcessController(@NonNull ActivityTaskManagerService atm,
             @NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
             @NonNull WindowProcessListener listener) {
@@ -349,9 +348,6 @@
         mUseFifoUiScheduling = com.android.window.flags.Flags.fifoPriorityForMajorUiProcesses()
                 && (isSysUiPackage || mAtm.isCallerRecents(uid));
 
-        mCanUseSystemGrammaticalGender = mAtm.mGrammaticalManagerInternal != null
-                && mAtm.mGrammaticalManagerInternal.canGetSystemGrammaticalGender(mUid,
-                mInfo.packageName);
         onConfigurationChanged(atm.getGlobalConfiguration());
         mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
     }
@@ -1612,11 +1608,6 @@
             return;
         }
 
-        if (mCanUseSystemGrammaticalGender) {
-            config.setGrammaticalGender(
-                    mAtm.mGrammaticalManagerInternal.getSystemGrammaticalGender(mUserId));
-        }
-
         if (mPauseConfigurationDispatchCount > 0) {
             mHasPendingConfigurationChange = true;
             return;
@@ -2139,4 +2130,34 @@
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         mListener.dumpDebug(proto, fieldId);
     }
+
+    @Override
+    protected boolean setOverrideGender(Configuration requestsTmpConfig, int gender) {
+        return applyConfigGenderOverride(requestsTmpConfig, gender,
+                mAtm.mGrammaticalManagerInternal, mUid);
+    }
+
+    static boolean applyConfigGenderOverride(@NonNull Configuration overrideConfig,
+            @Configuration.GrammaticalGender int override,
+            GrammaticalInflectionManagerInternal service, int uid) {
+        final boolean canGetSystemValue = service != null
+                && service.canGetSystemGrammaticalGender(uid);
+
+        // The priority here is as follows:
+        // - app-specific override if set
+        // - system value if allowed to see it
+        // - global configuration otherwise
+        final int targetValue = (override != Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED)
+                ? override
+                : canGetSystemValue
+                        ? Configuration.GRAMMATICAL_GENDER_UNDEFINED
+                        : service != null
+                                ? service.getGrammaticalGenderFromDeveloperSettings()
+                                : Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
+        if (overrideConfig.getGrammaticalGenderRaw() == targetValue) {
+            return false;
+        }
+        overrideConfig.setGrammaticalGender(targetValue);
+        return true;
+    }
 }
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 4a8d73d2..07cda37 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -875,7 +875,7 @@
         }
 
         private void registerBroadcastReceivers() {
-            PackageMonitor monitor = new PackageMonitor() {
+            PackageMonitor monitor = new PackageMonitor(true) {
                 /**
                  * Checks if the package contains a print service.
                  *
diff --git a/services/tests/servicestests/src/com/android/server/grammaticalinflection/GrammaticalInflectionBackupTest.java b/services/tests/servicestests/src/com/android/server/grammaticalinflection/GrammaticalInflectionBackupTest.java
index af6f6f2..608b306 100644
--- a/services/tests/servicestests/src/com/android/server/grammaticalinflection/GrammaticalInflectionBackupTest.java
+++ b/services/tests/servicestests/src/com/android/server/grammaticalinflection/GrammaticalInflectionBackupTest.java
@@ -112,7 +112,7 @@
     public void testSystemBackupPayload_returnsGender()
             throws IOException, ClassNotFoundException {
         doReturn(Configuration.GRAMMATICAL_GENDER_MASCULINE).when(mGrammaticalInflectionService)
-                .getSystemGrammaticalGender(any(), eq(DEFAULT_USER_ID));
+                .getSystemGrammaticalGender(eq(DEFAULT_USER_ID));
 
         int gender = convertByteArrayToInt(mBackupHelper.getSystemBackupPayload(DEFAULT_USER_ID));
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 1d3dacc..9a92c70 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -408,6 +408,60 @@
     }
 
     @Test
+    public void setRcProfileRootMenu_reportFeatureBroadcast() {
+        setRcProfileSourceDeviceTestHelper(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED);
+    }
+
+    @Test
+    public void setRcProfileSetupMenu_reportFeatureBroadcast() {
+        setRcProfileSourceDeviceTestHelper(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED);
+    }
+
+    @Test
+    public void setRcProfileContentMenu_reportFeatureBroadcast() {
+        setRcProfileSourceDeviceTestHelper(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED);
+    }
+
+    @Test
+    public void setRcProfileTopMenu_reportFeatureBroadcast() {
+        setRcProfileSourceDeviceTestHelper(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED);
+    }
+
+    @Test
+    public void setRcProfileMediaSensitiveMenu_reportFeatureBroadcast() {
+        setRcProfileSourceDeviceTestHelper(
+                HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED);
+    }
+
+    /** Helper method to test if feature discovery message sent given RCProfile change */
+    private void setRcProfileSourceDeviceTestHelper(final String setting, final int val) {
+        mNativeWrapper.clearResultMessages();
+
+        mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0);
+        mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(setting, val);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(Constants.ADDR_PLAYBACK_1,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
+                mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
+                mPlaybackDeviceSpy.getDeviceFeatures());
+        assertThat(mNativeWrapper.getResultMessages()).contains(reportFeatures);
+    }
+
+    @Test
     public void disableAndReenableCec_volumeControlReturnsToOriginalValue_enabled() {
         int volumeControlEnabled = HdmiControlManager.VOLUME_CONTROL_ENABLED;
         mHdmiControlServiceSpy.setHdmiCecVolumeControlEnabledInternal(volumeControlEnabled);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java
index 920c376..eed9975 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java
@@ -16,11 +16,14 @@
 
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
+
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.os.Looper;
 import android.os.test.TestLooper;
@@ -35,6 +38,7 @@
 import org.junit.runners.JUnit4;
 
 import java.util.Collections;
+import java.util.List;
 
 /**
  * TV specific tests for {@link HdmiControlService} class.
@@ -47,6 +51,7 @@
     private static final String TAG = "HdmiControlServiceTvTest";
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
     private FakeNativeWrapper mNativeWrapper;
     private HdmiEarcController mHdmiEarcController;
     private FakeEarcNativeWrapper mEarcNativeWrapper;
@@ -90,6 +95,8 @@
         mHdmiControlService.initService();
 
         mTestLooper.dispatchAll();
+
+        mHdmiCecLocalDeviceTv = mHdmiControlService.tv();
     }
 
     @Test
@@ -139,4 +146,23 @@
         assertThat(mHdmiControlService
                 .verifyPhysicalAddresses(HdmiUtils.buildMessage("4F:82:10"))).isFalse();
     }
+
+    @Test
+    public void setRcProfileTv_reportFeatureBroadcast() {
+        mNativeWrapper.clearResultMessages();
+
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0);
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                HdmiControlManager.RC_PROFILE_TV_NONE);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(Constants.ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0, List.of(DEVICE_TV),
+                mHdmiCecLocalDeviceTv.getRcProfile(), mHdmiCecLocalDeviceTv.getRcFeatures(),
+                mHdmiCecLocalDeviceTv.getDeviceFeatures());
+        assertThat(mNativeWrapper.getResultMessages()).contains(reportFeatures);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 1da5001..b0a3374 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -525,10 +525,13 @@
 
         // The activity shouldn't start relaunching since it doesn't have any desk resources.
         assertFalse(activity.isRelaunching());
+        // The activity configuration ui mode should match.
+        final var activityConfig = activity.getConfiguration();
+        assertEquals(newConfig.uiMode, activityConfig.uiMode);
 
         // The configuration change is still sent to the activity, even if it doesn't relaunch.
         final ActivityConfigurationChangeItem expected =
-                ActivityConfigurationChangeItem.obtain(activity.token, newConfig,
+                ActivityConfigurationChangeItem.obtain(activity.token, activityConfig,
                         activity.getActivityWindowInfo());
         verify(mClientLifecycleManager).scheduleTransactionItem(
                 eq(activity.app.getThread()), eq(expected));
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 87f26e5..c45c86c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -56,6 +56,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManagerGlobal;
@@ -86,6 +87,7 @@
 import com.android.server.display.DisplayControl;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.firewall.IntentFirewall;
+import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
 import com.android.server.input.InputManagerService;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
@@ -208,6 +210,11 @@
         setUpLocalServices();
         setUpActivityTaskManagerService();
         setUpWindowManagerService();
+
+        // We never load the system settings in the tests, thus need to setup the grammatical
+        // gender configuration explicitly.
+        mAtmService.getGlobalConfiguration().setGrammaticalGender(
+                Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED);
     }
 
     private void setUpSystemCore() {
@@ -337,6 +344,18 @@
         };
         when(umi.isUserVisible(anyInt())).thenAnswer(isUserVisibleAnswer);
         when(umi.isUserVisible(anyInt(), anyInt())).thenAnswer(isUserVisibleAnswer);
+
+        final var gimi = mock(
+                GrammaticalInflectionManagerInternal.class, withSettings().stubOnly());
+        doReturn(Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED).when(
+                gimi).getGrammaticalGenderFromDeveloperSettings();
+        doReturn(Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED).when(
+                gimi).getSystemGrammaticalGender(anyInt());
+        doReturn(Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED).when(
+                gimi).mergedFinalSystemGrammaticalGender();
+        doReturn(false).when(gimi).canGetSystemGrammaticalGender(anyInt());
+        doReturn(gimi).when(
+                () -> LocalServices.getService(GrammaticalInflectionManagerInternal.class));
     }
 
     private void setUpActivityTaskManagerService() {
@@ -475,6 +494,7 @@
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy.class);
+        LocalServices.removeServiceForTest(GrammaticalInflectionManagerInternal.class);
     }
 
     Description getDescription() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index e5d7b40e..1d01420 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2538,7 +2538,8 @@
             }
         }
 
-        PackageMonitor mPackageMonitor = new PackageMonitor() {
+        PackageMonitor mPackageMonitor = new PackageMonitor(
+                /* supportsPackageRestartQuery= */ true) {
 
             @Override
             public boolean onHandleForceStop(Intent intent, String[] packages, int uid,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
index 23fa91c..2df3da6 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.testapp;
 
-import androidx.annotation.NonNull;
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
@@ -103,18 +102,13 @@
     }
 
     private static SplitPairRule createSplitPairRules(@NonNull String layoutDirection) {
-        final Set<SplitPairFilter> pairFilters = new HashSet<>();
-        final SplitPairFilter activitiesPair = new SplitPairFilter(
-                ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT,
-                ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT,
-                null /* secondaryActivityIntentAction */);
-        pairFilters.add(activitiesPair);
+        final Set<SplitPairFilter> pairFilters = getSplitPairFilters();
         final SplitAttributes splitAttributes = new SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.SPLIT_TYPE_EQUAL)
                 .setLayoutDirection(parseLayoutDirection(layoutDirection))
                 .build();
         // Setting thresholds to ALWAYS_ALLOW values to make it easy for running on all devices.
-        final SplitPairRule rule = new SplitPairRule.Builder(pairFilters)
+        return new SplitPairRule.Builder(pairFilters)
                 .setDefaultSplitAttributes(splitAttributes)
                 .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
                 .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
@@ -122,7 +116,22 @@
                 .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
                 .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
                 .build();
-        return rule;
+    }
+
+    @NonNull
+    private static Set<SplitPairFilter> getSplitPairFilters() {
+        final Set<SplitPairFilter> pairFilters = new HashSet<>();
+        final SplitPairFilter mainAndSecondaryActivitiesPair = new SplitPairFilter(
+                ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT,
+                ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT,
+                null /* secondaryActivityIntentAction */);
+        pairFilters.add(mainAndSecondaryActivitiesPair);
+        final SplitPairFilter mainAndTrampolineActivitiesPair = new SplitPairFilter(
+                ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT,
+                ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT,
+                null /* secondaryActivityIntentAction */);
+        pairFilters.add(mainAndTrampolineActivitiesPair);
+        return pairFilters;
     }
 
     private static SplitPlaceholderRule createSplitPlaceholderRules(
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index c0c60ef..d033389 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -393,6 +393,7 @@
                 mEditText.setFocusableInTouchMode(false);
             }
             rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+            rootView.setFitsSystemWindows(true);
             setContentView(rootView);
 
             if (requestFocus) {