Merge "Add a separated flag for Volume Panel large screen" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cc7d97a..2ff1f76 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14204,7 +14204,8 @@
     method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
-    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean);
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle);
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
     field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
diff --git a/core/java/Android.bp b/core/java/Android.bp
index eba500d..34b8a87 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -126,6 +126,7 @@
     srcs: [
         "android/os/IExternalVibrationController.aidl",
         "android/os/IExternalVibratorService.aidl",
+        "android/os/ExternalVibrationScale.aidl",
     ],
 }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 16c7753..39900a0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -74,6 +74,7 @@
 import android.os.UserManager;
 import android.permission.PermissionGroupUsage;
 import android.permission.PermissionUsageHelper;
+import android.permission.flags.Flags;
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -99,7 +100,6 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Parcelling;
 import com.android.internal.util.Preconditions;
-import com.android.media.flags.Flags;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -2077,7 +2077,8 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
+    @FlaggedApi(com.android.media.flags.Flags
+            .FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
     public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control";
 
     /**
@@ -2243,7 +2244,7 @@
      *
      * @hide
      */
-    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
     @SystemApi
     public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
             "android:access_restricted_settings";
@@ -3432,7 +3433,8 @@
             }
         }
 
-        public static final @android.annotation.NonNull Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
+        public static final @android.annotation.NonNull Creator<PackageOps> CREATOR =
+                new Creator<PackageOps>() {
             @Override public PackageOps createFromParcel(Parcel source) {
                 return new PackageOps(source);
             }
@@ -7410,7 +7412,7 @@
          * @param userId User id of the app whose Op changed.
          * @param persistentDeviceId persistent device id whose Op changed.
          */
-        @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
         default void onOpChanged(@NonNull String op, @NonNull String packageName, int userId,
                 @NonNull String persistentDeviceId) {
             if (Objects.equals(persistentDeviceId,
@@ -7480,7 +7482,7 @@
          * @param attributionFlags the attribution flags for this operation.
          * @param attributionChainId the unique id of the attribution chain this op is a part of.
          */
-        @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
         default void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
                 @Nullable String attributionTag, int virtualDeviceId, boolean active,
                 @AttributionFlags int attributionFlags, int attributionChainId) {
@@ -7534,7 +7536,7 @@
          * @param flags The flags of this op
          * @param result The result of the note.
          */
-        @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
         default void onOpNoted(@NonNull String op, int uid, @NonNull String packageName,
                 @Nullable String attributionTag, int virtualDeviceId, @OpFlags int flags,
                 @Mode int result) {
@@ -8361,14 +8363,26 @@
                         String attributionTag, int virtualDeviceId, boolean active,
                         @AttributionFlags int attributionFlags, int attributionChainId) {
                     executor.execute(() -> {
-                        if (callback instanceof OnOpActiveChangedInternalListener) {
-                            ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
-                                    uid, packageName, virtualDeviceId, active);
-                        }
-                        if (sAppOpInfos[op].name != null) {
-                            callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
-                                    attributionTag, virtualDeviceId, active, attributionFlags,
-                                    attributionChainId);
+                        if (Flags.deviceAwarePermissionApisEnabled()) {
+                            if (callback instanceof OnOpActiveChangedInternalListener) {
+                                ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+                                        uid, packageName, virtualDeviceId, active);
+                            }
+                            if (sAppOpInfos[op].name != null) {
+                                callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
+                                        attributionTag, virtualDeviceId, active, attributionFlags,
+                                        attributionChainId);
+                            }
+                        } else {
+                            if (callback instanceof OnOpActiveChangedInternalListener) {
+                                ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+                                        uid, packageName, active);
+                            }
+                            if (sAppOpInfos[op].name != null) {
+                                callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
+                                        attributionTag, active, attributionFlags,
+                                        attributionChainId);
+                            }
                         }
                     });
                 }
@@ -8613,9 +8627,13 @@
                     try {
                         executor.execute(() -> {
                             if (sAppOpInfos[op].name != null) {
-                                listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
-                                        attributionTag, virtualDeviceId,
-                                        flags, mode);
+                                if (Flags.deviceAwarePermissionApisEnabled()) {
+                                    listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+                                            attributionTag, virtualDeviceId, flags, mode);
+                                } else {
+                                    listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+                                            attributionTag, flags, mode);
+                                }
                             }
                         });
                     } finally {
diff --git a/core/java/android/os/ExternalVibrationScale.aidl b/core/java/android/os/ExternalVibrationScale.aidl
new file mode 100644
index 0000000..cf6f8ed
--- /dev/null
+++ b/core/java/android/os/ExternalVibrationScale.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * ExternalVibrationScale holds the vibration scale level and adaptive haptics scale. These
+ * can be used to scale external vibrations.
+ *
+ * @hide
+ */
+parcelable ExternalVibrationScale {
+    @Backing(type="int")
+    enum ScaleLevel {
+        SCALE_MUTE = -100,
+        SCALE_VERY_LOW = -2,
+        SCALE_LOW = -1,
+        SCALE_NONE = 0,
+        SCALE_HIGH = 1,
+        SCALE_VERY_HIGH = 2
+    }
+
+    /**
+     * The scale level that will be applied to external vibrations.
+     */
+    ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE;
+
+    /**
+     * The adaptive haptics scale that will be applied to external vibrations.
+     */
+    float adaptiveHapticsScale = 1f;
+}
diff --git a/core/java/android/os/IExternalVibratorService.aidl b/core/java/android/os/IExternalVibratorService.aidl
index 666171f..a9df15a 100644
--- a/core/java/android/os/IExternalVibratorService.aidl
+++ b/core/java/android/os/IExternalVibratorService.aidl
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
 
 /**
  * The communication channel by which an external system that wants to control the system
@@ -32,29 +33,24 @@
  * {@hide}
  */
 interface IExternalVibratorService {
-    const int SCALE_MUTE = -100;
-    const int SCALE_VERY_LOW = -2;
-    const int SCALE_LOW = -1;
-    const int SCALE_NONE = 0;
-    const int SCALE_HIGH = 1;
-    const int SCALE_VERY_HIGH = 2;
-
     /**
      * A method called by the external system to start a vibration.
      *
-     * If this returns {@code SCALE_MUTE}, then the vibration should <em>not</em> play. If this
-     * returns any other scale level, then any currently playing vibration controlled by the
-     * requesting system must be muted and this vibration can begin playback.
+     * This returns an {@link ExternalVibrationScale} which includes the vibration scale level and
+     * the adaptive haptics scale.
+     *
+     * If the returned scale level is {@link ExternalVibrationScale.ScaleLevel#SCALE_MUTE}, then
+     * the vibration should <em>not</em> play. If it returns any other scale level, then
+     * any currently playing vibration controlled by the requesting system must be muted and this
+     * vibration can begin playback.
      *
      * Note that the IExternalVibratorService implementation will not call mute on any currently
      * playing external vibrations in order to avoid re-entrancy with the system on the other side.
      *
-     * @param vibration An ExternalVibration
-     *
-     * @return {@code SCALE_MUTE} if the external vibration should not play, and any other scale
-     *         level if it should.
+     * @param vib The external vibration starting.
+     * @return {@link ExternalVibrationScale} including scale level and adaptive haptics scale.
      */
-    int onExternalVibrationStart(in ExternalVibration vib);
+    ExternalVibrationScale onExternalVibrationStart(in ExternalVibration vib);
 
     /**
      * A method called by the external system when a vibration no longer wants to play.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ec4d587..50adc40 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12481,6 +12481,24 @@
         public static void setLocationProviderEnabled(ContentResolver cr,
                 String provider, boolean enabled) {
         }
+
+        /**
+         * List of system components that support restore in a  V-> U OS downgrade but do not have
+         * RestoreAnyVersion set to true. Value set before system restore.
+         * This setting is not B&Rd
+         * List is stored as a comma-separated string of package names e.g. "a,b,c"
+         * @hide
+         */
+        public static final String V_TO_U_RESTORE_ALLOWLIST = "v_to_u_restore_allowlist";
+
+        /**
+         * List of system components that have RestoreAnyVersion set to true but do not support
+         * restore in a  V-> U OS downgrade. Value set before system restore.
+         * This setting is not B&Rd
+         * List is stored as a comma-separated string of package names e.g. "a,b,c"
+         * @hide
+         */
+        public static final String V_TO_U_RESTORE_DENYLIST = "v_to_u_restore_denylist";
     }
 
     /**
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 021bbf7..896b3f4 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -196,8 +196,6 @@
     private Canvas mCanvas;
     private int mSaveCount;
 
-    @Surface.FrameRateCompatibility int mFrameRateCompatibility;
-
     private final Object[] mNativeWindowLock = new Object[0];
     // Set by native code, do not write!
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 06dc275..596c52d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24,6 +24,7 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -2368,6 +2369,9 @@
 
     private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
     private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
+    // Used to set frame rate compatibility.
+    @Surface.FrameRateCompatibility int mFrameRateCompatibility =
+            FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 
     static {
         EMPTY_STATE_SET = StateSet.get(0);
@@ -10889,8 +10893,11 @@
             structure.setAutofillId(new AutofillId(getAutofillId(),
                     AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
         }
-        structure.setCredentialManagerRequest(getCredentialManagerRequest(),
-                getCredentialManagerCallback());
+        if (getViewCredentialHandler() != null) {
+            structure.setCredentialManagerRequest(
+                    getViewCredentialHandler().getRequest(),
+                    getViewCredentialHandler().getCallback());
+        }
         CharSequence cname = info.getClassName();
         structure.setClassName(cname != null ? cname.toString() : null);
         structure.setContentDescription(info.getContentDescription());
@@ -33538,7 +33545,8 @@
                         frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
                     }
                 } else {
-                    viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
+                    viewRootImpl.votePreferredFrameRate(mPreferredFrameRate,
+                            mFrameRateCompatibility);
                     return;
                 }
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1e79786..94260b2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -30,6 +30,7 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -1027,6 +1028,13 @@
     // as needed and can be useful for power saving.
     // Should not enable the dVRR feature if the value is false.
     private boolean mIsFrameRatePowerSavingsBalanced = true;
+    // Used to check if there is a conflict between different frame rate voting.
+    // Take 24 and 30 as an example, 24 is not a divisor of 30.
+    // We consider there is a conflict.
+    private boolean mIsFrameRateConflicted = false;
+    // Used to set frame rate compatibility.
+    @Surface.FrameRateCompatibility int mFrameRateCompatibility =
+            FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
     // time for touch boost period.
     private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
     // time for checking idle status periodically.
@@ -4110,6 +4118,7 @@
                 ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
         mPreferredFrameRate = -1;
+        mIsFrameRateConflicted = false;
     }
 
     private void createSyncIfNeeded() {
@@ -6508,6 +6517,7 @@
                     break;
                 case MSG_FRAME_RATE_SETTING:
                     mPreferredFrameRate = 0;
+                    mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
                     setPreferredFrameRate(mPreferredFrameRate);
                     break;
             }
@@ -12286,9 +12296,15 @@
         if (!shouldSetFrameRateCategory()) {
             return;
         }
+        int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+        if (mIsFrameRateConflicted) {
+            categoryFromConflictedFrameRates = mPreferredFrameRate > 60
+                    ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL;
+        }
 
         int frameRateCategory = mIsTouchBoosting
-                ? FRAME_RATE_CATEGORY_HIGH_HINT : preferredFrameRateCategory;
+                ? FRAME_RATE_CATEGORY_HIGH_HINT
+                : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates);
 
         // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
         // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
@@ -12338,7 +12354,7 @@
                                 + preferredFrameRate);
                 }
                 mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
-                    Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).applyAsyncUnsafe();
+                    mFrameRateCompatibility).applyAsyncUnsafe();
                 mLastPreferredFrameRate = preferredFrameRate;
             }
         } catch (Exception e) {
@@ -12361,7 +12377,8 @@
 
     private boolean shouldSetFrameRate() {
         // use toolkitSetFrameRate flag to gate the change
-        return mSurface.isValid() && mPreferredFrameRate > 0 && shouldEnableDvrr();
+        return mSurface.isValid() && mPreferredFrameRate > 0
+                && shouldEnableDvrr() && !mIsFrameRateConflicted;
     }
 
     private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12404,29 +12421,45 @@
     }
 
     /**
-     * Allow Views to vote for the preferred frame rate
+     * Allow Views to vote for the preferred frame rate and compatibility.
      * When determining the preferred frame rate value,
      * we follow this logic: If no preferred frame rate has been set yet,
      * we assign the value of frameRate as the preferred frame rate.
-     * If either the current or the new preferred frame rate exceeds 60 Hz,
-     * we select the higher value between them.
-     * However, if both values are 60 Hz or lower, we set the preferred frame rate
-     * to 60 Hz to maintain optimal performance.
+     * IF there are multiple frame rates are voted:
+     * 1. There is a frame rate is a multiple of all other frame rates.
+     * We choose this frmae rate to be the one to be set.
+     * 2. There is no frame rate can be a multiple of others
+     * We set category to HIGH if the maximum frame rate is greater than 60.
+     * Otherwise, we set category to NORMAL.
+     *
+     * Use FRAME_RATE_COMPATIBILITY_GTE for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+     * for TextureView video play and user requested frame rate.
      *
      * @param frameRate the preferred frame rate of a View
+     * @param frameRateCompatibility the preferred frame rate compatibility of a View
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
-    public void votePreferredFrameRate(float frameRate) {
+    public void votePreferredFrameRate(float frameRate, int frameRateCompatibility) {
         if (frameRate <= 0) {
             return;
         }
+        if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0
+                && frameRate % mPreferredFrameRate != 0) {
+            mIsFrameRateConflicted = true;
+            mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+        }
+        if (frameRate > mPreferredFrameRate) {
+            mFrameRateCompatibility = frameRateCompatibility;
+        }
 
         mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
-
         mHasInvalidation = true;
-        mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
-        mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
-                FRAME_RATE_SETTING_REEVALUATE_TIME);
+
+        if (!mIsFrameRateConflicted) {
+            mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+            mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+                    FRAME_RATE_SETTING_REEVALUATE_TIME);
+        }
     }
 
     /**
@@ -12454,6 +12487,14 @@
     }
 
     /**
+     * Get the value of mFrameRateCompatibility
+     */
+    @VisibleForTesting
+    public int getFrameRateCompatibility() {
+        return mFrameRateCompatibility;
+    }
+
+    /**
      * Get the value of mIsFrameRateBoosting
      */
     @VisibleForTesting
@@ -12503,6 +12544,15 @@
     }
 
     /**
+     * Get the value of mIsFrameRateConflicted
+     * Can be used to checked if there is a conflict of frame rate votes.
+     */
+    @VisibleForTesting
+    public boolean isFrameRateConflicted() {
+        return mIsFrameRateConflicted;
+    }
+
+    /**
      * Set the value of mIsFrameRatePowerSavingsBalanced
      * Can be used to checked if toolkit dVRR feature is enabled.
      */
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 55986e7..8d3920f 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -78,3 +78,11 @@
     bug: "300979854"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "predictive_back_ime"
+    namespace: "input_method"
+    description: "Predictive back animation for IMEs"
+    bug: "322836622"
+    is_fixed_read_only: true
+}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 038c00e..64cbe7f 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -24,6 +24,8 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -705,17 +707,51 @@
     public void votePreferredFrameRate_voteFrameRate_aggregate() {
         View view = new View(sContext);
         attachViewToWindow(view);
+        ViewRootImpl viewRootImpl = view.getViewRootImpl();
         sInstrumentation.runOnMainSync(() -> {
-            ViewRootImpl viewRootImpl = view.getViewRootImpl();
             assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
-            viewRootImpl.votePreferredFrameRate(24);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+            viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
             assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
-            viewRootImpl.votePreferredFrameRate(30);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_GTE);
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+            viewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
             assertEquals(viewRootImpl.getPreferredFrameRate(), 30, 0.1);
-            viewRootImpl.votePreferredFrameRate(60);
+            // If there is a conflict, then set compatibility to
+            // FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            // Should be true since there is a conflict between 24 and 30.
+            assertEquals(viewRootImpl.isFrameRateConflicted(), true);
+            view.invalidate();
+        });
+        sInstrumentation.waitForIdleSync();
+
+        sInstrumentation.runOnMainSync(() -> {
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+            viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
             assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
-            viewRootImpl.votePreferredFrameRate(120);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_GTE);
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+            viewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
             assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            // Should be false since 60 is a divisor of 120.
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+            viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+            // compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+            // since the frame rate 60 is smaller than 120.
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            // Should be false since 60 is a divisor of 120.
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+
         });
     }
 
@@ -842,14 +878,26 @@
 
         sInstrumentation.runOnMainSync(() -> {
             assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
-            viewRootImpl.votePreferredFrameRate(24);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+            viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
             assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
             view.invalidate();
             assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+            assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+            assertEquals(viewRootImpl.isFrameRateConflicted(), false);
         });
 
         Thread.sleep(delay);
         assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+        assertEquals(viewRootImpl.getFrameRateCompatibility(),
+                FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+        assertEquals(viewRootImpl.isFrameRateConflicted(), false);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index e0f0556..369258e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -683,6 +683,17 @@
         mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
     }
 
+    /** Called when sensitive notification state has changed */
+    public void onSensitiveNotificationProtectionStateChanged(
+            boolean sensitiveNotificationProtectionActive) {
+        if (mStackView != null) {
+            mStackView.onSensitiveNotificationProtectionStateChanged(
+                    sensitiveNotificationProtectionActive);
+            ProtoLog.d(WM_SHELL_BUBBLES, "onSensitiveNotificationProtectionStateChanged=%b",
+                    sensitiveNotificationProtectionActive);
+        }
+    }
+
     /** Whether bubbles are showing in the bubble bar. */
     public boolean isShowingAsBubbleBar() {
         return canShowAsBubbleBar() && mBubbleStateListener != null;
@@ -2583,6 +2594,14 @@
             mMainExecutor.execute(
                     () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
         }
+
+        @Override
+        public void onSensitiveNotificationProtectionStateChanged(
+                boolean sensitiveNotificationProtectionActive) {
+            mMainExecutor.execute(
+                    () -> BubbleController.this.onSensitiveNotificationProtectionStateChanged(
+                            sensitiveNotificationProtectionActive));
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index b23fd52..e7da034 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -291,6 +291,11 @@
      */
     private boolean mRemovingLastBubbleWhileExpanded = false;
 
+    /**
+     * Whether sensitive notification protection should disable flyout
+     */
+    private boolean mSensitiveNotificationProtectionActive = false;
+
     /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
     private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
 
@@ -2199,6 +2204,11 @@
         }
     }
 
+    void onSensitiveNotificationProtectionStateChanged(
+            boolean sensitiveNotificationProtectionActive) {
+        mSensitiveNotificationProtectionActive = sensitiveNotificationProtectionActive;
+    }
+
     /**
      * Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or
      * not.
@@ -2842,6 +2852,7 @@
                 || isExpanded()
                 || mIsExpansionAnimating
                 || mIsGestureInProgress
+                || mSensitiveNotificationProtectionActive
                 || mBubbleToExpandAfterFlyoutCollapse != null
                 || bubbleView == null) {
             if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 28af0ca..26077cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -286,6 +286,16 @@
     void onUserRemoved(int removedUserId);
 
     /**
+     * Called when the Sensitive notification protection state has changed, such as when media
+     * projection starts and stops.
+     *
+     * @param sensitiveNotificationProtectionActive {@code true} if notifications should be
+     *     protected
+     */
+    void onSensitiveNotificationProtectionStateChanged(
+            boolean sensitiveNotificationProtectionActive);
+
+    /**
      * A listener to be notified of bubble state changes, used by launcher to render bubbles in
      * its process.
      */
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e4f3e2d..abd84de 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -155,6 +155,7 @@
         host: {
             static_libs: [
                 "libandroidfw",
+                "libhostgraphics",
                 "libutils",
             ],
         },
@@ -501,6 +502,17 @@
     ],
     header_libs: ["android_graphics_jni_headers"],
     export_header_lib_headers: ["android_graphics_jni_headers"],
+    target: {
+        android: {
+            export_include_dirs: ["platform/android"],
+        },
+        host: {
+            export_include_dirs: ["platform/host"],
+        },
+        windows: {
+            enabled: true,
+        },
+    },
 }
 
 cc_defaults {
@@ -538,6 +550,7 @@
         "utils/Blur.cpp",
         "utils/Color.cpp",
         "utils/LinearAllocator.cpp",
+        "utils/StringUtils.cpp",
         "utils/TypefaceUtils.cpp",
         "utils/VectorDrawableUtils.cpp",
         "AnimationContext.cpp",
@@ -552,6 +565,7 @@
         "Mesh.cpp",
         "MemoryPolicy.cpp",
         "PathParser.cpp",
+        "ProfileData.cpp",
         "Properties.cpp",
         "PropertyValuesAnimatorSet.cpp",
         "PropertyValuesHolder.cpp",
@@ -569,12 +583,13 @@
         export_proto_headers: true,
     },
 
+    header_libs: ["libandroid_headers_private"],
+
     target: {
         android: {
-            header_libs: [
-                "libandroid_headers_private",
-                "libtonemap_headers",
-            ],
+            header_libs: ["libtonemap_headers"],
+
+            local_include_dirs: ["platform/android"],
 
             srcs: [
                 "hwui/AnimatedImageThread.cpp",
@@ -605,7 +620,6 @@
                 "thread/CommonPool.cpp",
                 "utils/GLUtils.cpp",
                 "utils/NdkUtils.cpp",
-                "utils/StringUtils.cpp",
                 "AutoBackendTextureRelease.cpp",
                 "DeferredLayerUpdater.cpp",
                 "DeviceInfo.cpp",
@@ -617,7 +631,6 @@
                 "FrameMetricsReporter.cpp",
                 "Layer.cpp",
                 "LayerUpdateQueue.cpp",
-                "ProfileData.cpp",
                 "ProfileDataContainer.cpp",
                 "Readback.cpp",
                 "TreeInfo.cpp",
@@ -628,6 +641,21 @@
             // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
             cflags: ["-Wno-implicit-fallthrough"],
         },
+        host: {
+            header_libs: ["libnativebase_headers"],
+
+            local_include_dirs: ["platform/host"],
+
+            srcs: [
+                "platform/host/renderthread/CacheManager.cpp",
+                "platform/host/renderthread/RenderThread.cpp",
+                "platform/host/ProfileDataContainer.cpp",
+                "platform/host/Readback.cpp",
+                "platform/host/WebViewFunctorManager.cpp",
+            ],
+
+            cflags: ["-Wno-unused-private-field"],
+        },
     },
 }
 
@@ -663,6 +691,7 @@
     header_libs: ["libandroid_headers_private"],
     target: {
         android: {
+            local_include_dirs: ["platform/android"],
             shared_libs: [
                 "libgui",
                 "libui",
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 3d0ca0a..7be9541 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -110,6 +110,7 @@
 }
 
 void ProfileData::dump(int fd) const {
+#ifdef __ANDROID__
     dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
     dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
     dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
@@ -138,6 +139,7 @@
         dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount);
     });
     dprintf(fd, "\n");
+#endif
 }
 
 uint32_t ProfileData::findPercentile(int percentile) const {
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/platform/android/thread/ThreadBase.h
similarity index 98%
rename from libs/hwui/thread/ThreadBase.h
rename to libs/hwui/platform/android/thread/ThreadBase.h
index 0289d3f..2f3581f 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/platform/android/thread/ThreadBase.h
@@ -17,14 +17,14 @@
 #ifndef HWUI_THREADBASE_H
 #define HWUI_THREADBASE_H
 
-#include "WorkQueue.h"
-#include "utils/Macros.h"
-
 #include <utils/Looper.h>
 #include <utils/Thread.h>
 
 #include <algorithm>
 
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
 namespace android::uirenderer {
 
 class ThreadBase : public Thread {
diff --git a/libs/hwui/platform/host/ProfileDataContainer.cpp b/libs/hwui/platform/host/ProfileDataContainer.cpp
new file mode 100644
index 0000000..9ed1b02
--- /dev/null
+++ b/libs/hwui/platform/host/ProfileDataContainer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProfileDataContainer.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace uirenderer {
+
+void ProfileDataContainer::freeData() REQUIRES(mJankDataMutex) {
+    delete mData;
+    mIsMapped = false;
+    mData = nullptr;
+}
+
+void ProfileDataContainer::rotateStorage() {
+    std::lock_guard lock(mJankDataMutex);
+    mData->reset();
+}
+
+void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+    ALOGE("Ashmem is not supported for non-Android configurations");
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/Readback.cpp b/libs/hwui/platform/host/Readback.cpp
new file mode 100644
index 0000000..b024ec0
--- /dev/null
+++ b/libs/hwui/platform/host/Readback.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+    return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+    return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
+    return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
+                                   SkBitmap* bitmap) {
+    return CopyResult::UnknownError;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+                             SkBitmap* bitmap) {
+    return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
new file mode 100644
index 0000000..1d16655
--- /dev/null
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WebViewFunctorManager.h"
+
+namespace android::uirenderer {
+
+WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+                               RenderMode functorMode)
+        : mData(data) {}
+
+WebViewFunctor::~WebViewFunctor() {}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {}
+
+void WebViewFunctor::onRemovedFromTree() {}
+
+bool WebViewFunctor::prepareRootSurfaceControl() {
+    return true;
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {}
+
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {}
+
+void WebViewFunctor::postDrawVk() {}
+
+void WebViewFunctor::destroyContext() {}
+
+void WebViewFunctor::removeOverlays() {}
+
+ASurfaceControl* WebViewFunctor::getSurfaceControl() {
+    return mSurfaceControl;
+}
+
+void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
+
+void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+    static WebViewFunctorManager sInstance;
+    return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+                                         RenderMode functorMode) {
+    return 0;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {}
+
+void WebViewFunctorManager::onContextDestroyed() {}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+    return nullptr;
+}
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/platform/host/renderthread/CacheManager.cpp b/libs/hwui/platform/host/renderthread/CacheManager.cpp
new file mode 100644
index 0000000..b03f400
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/CacheManager.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 "renderthread/CacheManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+CacheManager::CacheManager(RenderThread& thread)
+        : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {}
+
+void CacheManager::setupCacheLimits() {}
+
+void CacheManager::destroy() {}
+
+void CacheManager::trimMemory(TrimLevel mode) {}
+
+void CacheManager::trimCaches(CacheTrimLevel mode) {}
+
+void CacheManager::trimStaleResources() {}
+
+void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {}
+
+void CacheManager::onFrameCompleted() {}
+
+void CacheManager::onThreadIdle() {}
+
+void CacheManager::scheduleDestroyContext() {}
+
+void CacheManager::cancelDestroyContext() {}
+
+bool CacheManager::areAllContextsStopped() {
+    return false;
+}
+
+void CacheManager::checkUiHidden() {}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {}
+
+void CacheManager::onContextStopped(CanvasContext* context) {}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
new file mode 100644
index 0000000..6f08b59
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "renderthread/RenderThread.h"
+
+#include "Readback.h"
+#include "renderthread/VulkanManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+static bool gHasRenderThreadInstance = false;
+static JVMAttachHook gOnStartHook = nullptr;
+
+ASurfaceControlFunctions::ASurfaceControlFunctions() {}
+
+bool RenderThread::hasInstance() {
+    return gHasRenderThreadInstance;
+}
+
+void RenderThread::setOnStartHook(JVMAttachHook onStartHook) {
+    LOG_ALWAYS_FATAL_IF(hasInstance(), "can't set an onStartHook after we've started...");
+    gOnStartHook = onStartHook;
+}
+
+JVMAttachHook RenderThread::getOnStartHook() {
+    return gOnStartHook;
+}
+
+RenderThread& RenderThread::getInstance() {
+    [[clang::no_destroy]] static sp<RenderThread> sInstance = []() {
+        sp<RenderThread> thread = sp<RenderThread>::make();
+        thread->start("RenderThread");
+        return thread;
+    }();
+    gHasRenderThreadInstance = true;
+    return *sInstance;
+}
+
+RenderThread::RenderThread()
+        : ThreadBase()
+        , mVsyncSource(nullptr)
+        , mVsyncRequested(false)
+        , mFrameCallbackTaskPending(false)
+        , mRenderState(nullptr)
+        , mEglManager(nullptr)
+        , mFunctorManager(WebViewFunctorManager::instance())
+        , mGlobalProfileData(mJankDataMutex) {
+    Properties::load();
+}
+
+RenderThread::~RenderThread() {}
+
+void RenderThread::initThreadLocals() {
+    mCacheManager = new CacheManager(*this);
+}
+
+void RenderThread::requireGlContext() {}
+
+void RenderThread::requireVkContext() {}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {}
+
+void RenderThread::destroyRenderingContext() {}
+
+VulkanManager& RenderThread::vulkanManager() {
+    return *mVkManager;
+}
+
+void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) {}
+
+void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+Readback& RenderThread::readback() {
+    if (!mReadback) {
+        mReadback = new Readback(*this);
+    }
+
+    return *mReadback;
+}
+
+void RenderThread::setGrContext(sk_sp<GrDirectContext> context) {}
+
+sk_sp<GrDirectContext> RenderThread::requireGrContext() {
+    return mGrContext;
+}
+
+bool RenderThread::threadLoop() {
+    if (gOnStartHook) {
+        gOnStartHook("RenderThread");
+    }
+    initThreadLocals();
+
+    while (true) {
+        waitForWork();
+        processQueue();
+        mCacheManager->onThreadIdle();
+    }
+
+    return false;
+}
+
+void RenderThread::postFrameCallback(IFrameCallback* callback) {}
+
+bool RenderThread::removeFrameCallback(IFrameCallback* callback) {
+    return false;
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {}
+
+sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
+    return nullptr;
+}
+
+bool RenderThread::isCurrent() {
+    return true;
+}
+
+void RenderThread::preload() {}
+
+void RenderThread::trimMemory(TrimLevel level) {}
+
+void RenderThread::trimCaches(CacheTrimLevel level) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/platform/host/thread/ThreadBase.h
similarity index 65%
copy from libs/hwui/thread/ThreadBase.h
copy to libs/hwui/platform/host/thread/ThreadBase.h
index 0289d3f..d709430 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/platform/host/thread/ThreadBase.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,31 +17,24 @@
 #ifndef HWUI_THREADBASE_H
 #define HWUI_THREADBASE_H
 
-#include "WorkQueue.h"
-#include "utils/Macros.h"
-
-#include <utils/Looper.h>
 #include <utils/Thread.h>
 
 #include <algorithm>
 
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
 namespace android::uirenderer {
 
 class ThreadBase : public Thread {
     PREVENT_COPY_AND_ASSIGN(ThreadBase);
 
 public:
-    ThreadBase()
-            : Thread(false)
-            , mLooper(new Looper(false))
-            , mQueue([this]() { mLooper->wake(); }, mLock) {}
+    ThreadBase() : Thread(false), mQueue([this]() { mCondition.notify_all(); }, mLock) {}
 
     WorkQueue& queue() { return mQueue; }
 
-    void requestExit() {
-        Thread::requestExit();
-        mLooper->wake();
-    }
+    void requestExit() { Thread::requestExit(); }
 
     void start(const char* name = "ThreadBase") { Thread::run(name); }
 
@@ -51,37 +44,31 @@
 
 protected:
     void waitForWork() {
-        nsecs_t nextWakeup;
-        {
-            std::unique_lock lock{mLock};
-            nextWakeup = mQueue.nextWakeup(lock);
-        }
-        int timeout = -1;
+        std::unique_lock lock{mLock};
+        nsecs_t nextWakeup = mQueue.nextWakeup(lock);
+        std::chrono::nanoseconds duration = std::chrono::nanoseconds::max();
         if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
-            timeout = ns2ms(nextWakeup - WorkQueue::clock::now());
+            int timeout = nextWakeup - WorkQueue::clock::now();
             if (timeout < 0) timeout = 0;
+            duration = std::chrono::nanoseconds(timeout);
         }
-        int result = mLooper->pollOnce(timeout);
-        LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!");
+        mCondition.wait_for(lock, duration);
     }
 
     void processQueue() { mQueue.process(); }
 
     virtual bool threadLoop() override {
-        Looper::setForThread(mLooper);
         while (!exitPending()) {
             waitForWork();
             processQueue();
         }
-        Looper::setForThread(nullptr);
         return false;
     }
 
-    sp<Looper> mLooper;
-
 private:
     WorkQueue mQueue;
     std::mutex mLock;
+    std::condition_variable mCondition;
 };
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index 22ae59e..493c943 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -17,15 +17,7 @@
 #ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
 #define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
 
-#ifdef __ANDROID__  // Layoutlib does not support surface control
 #include <android/surface_control.h>
-#else
-// To avoid ifdefs around overlay implementation all over the place we typedef these to void *. They
-// won't be used.
-typedef void* ASurfaceControl;
-typedef void* ASurfaceTransaction;
-#endif
-
 #include <cutils/compiler.h>
 #include <private/hwui/DrawGlInfo.h>
 #include <private/hwui/DrawVkInfo.h>
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 623ee4e..a024aeb 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,11 +17,12 @@
 #include "RenderThread.h"
 
 #include <GrContextOptions.h>
-#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
 #include <android-base/properties.h>
 #include <dlfcn.h>
 #include <gl/GrGLInterface.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <private/android/choreographer.h>
 #include <sys/resource.h>
 #include <ui/FatVector.h>
 #include <utils/Condition.h>
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 79e57de..045d26f 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -20,10 +20,7 @@
 #include <GrDirectContext.h>
 #include <SkBitmap.h>
 #include <cutils/compiler.h>
-#include <private/android/choreographer.h>
 #include <surface_control_private.h>
-#include <thread/ThreadBase.h>
-#include <utils/Looper.h>
 #include <utils/Thread.h>
 
 #include <memory>
diff --git a/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
new file mode 100644
index 0000000..6186773
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
@@ -0,0 +1,87 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:autoMirrored="true"
+        android:height="24dp"
+        android:width="24dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M16.984,24H7.279L12.131,15.508L16.984,24ZM10.481,22.144H13.781L12.131,19.257L10.481,22.144Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M12.131,14.295C13.471,14.295 14.558,13.209 14.558,11.869C14.558,10.529 13.471,9.442 12.131,9.442C10.791,9.442 9.705,10.529 9.705,11.869C9.705,13.209 10.791,14.295 12.131,14.295Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M4.573,21.368C4.052,20.943 3.967,20.179 4.379,19.657C4.804,19.136 5.568,19.051 6.09,19.463C6.611,19.876 6.696,20.64 6.284,21.174C6.041,21.465 5.689,21.623 5.338,21.623C5.071,21.623 4.804,21.538 4.573,21.368Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M17.991,21.162C17.579,20.628 17.663,19.876 18.185,19.451C18.707,19.039 19.471,19.124 19.896,19.646C20.308,20.167 20.223,20.931 19.702,21.344C19.471,21.526 19.204,21.611 18.949,21.611C18.586,21.611 18.234,21.453 17.991,21.162Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M1.213,17.145C0.91,16.551 1.165,15.823 1.771,15.532C2.378,15.241 3.093,15.495 3.397,16.09C3.688,16.697 3.433,17.424 2.827,17.715C2.657,17.8 2.475,17.837 2.305,17.837C1.844,17.837 1.419,17.582 1.213,17.145Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M21.449,17.691C20.842,17.4 20.588,16.684 20.879,16.077C21.17,15.471 21.898,15.216 22.504,15.507C23.099,15.798 23.354,16.526 23.062,17.133C22.856,17.557 22.419,17.812 21.971,17.812C21.789,17.812 21.619,17.776 21.449,17.691Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M0,11.892C0,11.225 0.546,10.679 1.213,10.679C1.88,10.679 2.426,11.212 2.426,11.892C2.426,12.559 1.88,13.105 1.213,13.105C0.546,13.105 0,12.559 0,11.892Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M21.837,11.869C21.837,11.857 21.837,11.845 21.837,11.833C21.824,11.153 22.37,10.62 23.05,10.607C23.717,10.607 24.251,11.153 24.263,11.821C24.263,11.833 24.263,11.845 24.263,11.845C24.263,11.857 24.263,11.869 24.263,11.869C24.263,12.536 23.717,13.082 23.05,13.082C22.382,13.082 21.837,12.536 21.837,11.869Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M1.759,8.242C1.152,7.963 0.898,7.235 1.189,6.628C1.48,6.022 2.196,5.767 2.802,6.058C3.409,6.349 3.664,7.077 3.372,7.684C3.166,8.108 2.729,8.363 2.281,8.363C2.099,8.363 1.929,8.327 1.759,8.242Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M20.866,7.622C20.563,7.028 20.818,6.3 21.424,6.009C22.019,5.706 22.747,5.96 23.038,6.567C23.038,6.567 23.038,6.567 23.05,6.567C23.341,7.161 23.087,7.889 22.48,8.181C22.31,8.265 22.128,8.302 21.958,8.302C21.509,8.302 21.073,8.059 20.866,7.622Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M4.355,4.104C3.931,3.582 4.016,2.818 4.537,2.406C5.071,1.981 5.823,2.066 6.248,2.588C6.672,3.109 6.588,3.874 6.066,4.298C5.835,4.48 5.569,4.565 5.302,4.565C4.95,4.565 4.598,4.407 4.355,4.104Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M18.161,4.262C17.627,3.838 17.542,3.073 17.955,2.552C18.379,2.03 19.132,1.945 19.666,2.358C20.187,2.77 20.272,3.534 19.86,4.068C19.617,4.359 19.265,4.517 18.913,4.517C18.646,4.517 18.379,4.432 18.161,4.262Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M8.492,1.497C8.334,0.854 8.747,0.199 9.402,0.041C10.057,-0.105 10.7,0.308 10.858,0.963C11.003,1.606 10.591,2.261 9.948,2.407C9.851,2.431 9.754,2.443 9.669,2.443C9.123,2.443 8.613,2.067 8.492,1.497Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M14.267,2.395C13.599,2.249 13.199,1.606 13.345,0.951C13.49,0.296 14.133,-0.116 14.788,0.029C15.443,0.175 15.856,0.83 15.71,1.485C15.589,2.043 15.08,2.431 14.534,2.431C14.437,2.431 14.352,2.419 14.267,2.395Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M7,17.037C6.527,16.564 6.527,15.8 7,15.326C7.473,14.841 8.237,14.841 8.71,15.314C9.196,15.787 9.196,16.552 8.723,17.025C8.48,17.267 8.177,17.389 7.861,17.389C7.546,17.389 7.242,17.267 7,17.037Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M15.565,17.012C15.092,16.539 15.092,15.762 15.565,15.289C16.038,14.816 16.814,14.816 17.288,15.289C17.761,15.762 17.761,16.539 17.288,17.012C17.045,17.243 16.742,17.364 16.426,17.364C16.111,17.364 15.807,17.243 15.565,17.012Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M4.853,11.917C4.853,11.237 5.386,10.691 6.054,10.691C6.721,10.691 7.279,11.225 7.279,11.892C7.279,12.56 6.745,13.106 6.078,13.118C5.398,13.118 4.853,12.584 4.853,11.917Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M16.984,11.868C16.984,11.856 16.984,11.844 16.984,11.832C16.984,11.832 16.984,11.82 16.984,11.807C16.972,11.14 17.506,10.582 18.185,10.582C18.852,10.57 19.398,11.116 19.41,11.783C19.41,11.795 19.41,11.82 19.41,11.832C19.41,11.844 19.41,11.856 19.41,11.868C19.41,12.535 18.865,13.081 18.197,13.081C17.53,13.081 16.984,12.535 16.984,11.868Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M6.952,8.471C6.478,7.997 6.478,7.233 6.952,6.76C6.952,6.76 6.952,6.76 6.939,6.76C7.413,6.275 8.189,6.275 8.662,6.748C9.135,7.221 9.147,7.985 8.674,8.458C8.432,8.701 8.116,8.822 7.813,8.822C7.497,8.822 7.194,8.701 6.952,8.471Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M15.529,8.399C15.043,7.938 15.043,7.161 15.504,6.688C15.977,6.203 16.742,6.203 17.227,6.664C17.7,7.137 17.712,7.901 17.239,8.387C17.009,8.629 16.693,8.751 16.378,8.751C16.075,8.751 15.759,8.629 15.529,8.399Z"/>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M10.87,5.815C10.858,5.148 11.392,4.59 12.071,4.59C12.738,4.578 13.284,5.124 13.284,5.791C13.296,6.458 12.762,7.016 12.083,7.016C11.416,7.016 10.87,6.483 10.87,5.815Z"/>
+</vector>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b58187d..28cdc6d 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -807,7 +807,9 @@
                  Settings.Secure.UI_TRANSLATION_ENABLED,
                  Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
                  Settings.Secure.DND_CONFIGS_MIGRATED,
-                 Settings.Secure.NAVIGATION_MODE_RESTORE);
+                 Settings.Secure.NAVIGATION_MODE_RESTORE,
+                 Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+                 Settings.Secure.V_TO_U_RESTORE_DENYLIST);
 
     @Test
     public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 71f9ba27..a69a2a6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -377,6 +377,13 @@
 }
 
 flag {
+    name: "screenshot_action_dismiss_system_windows"
+    namespace: "systemui"
+    description: "Dismiss existing system windows when starting action from screenshot UI"
+    bug: "309933761"
+}
+
+flag {
    name: "run_fingerprint_detect_on_dismissible_keyguard"
    namespace: "systemui"
    description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
@@ -529,3 +536,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "bind_keyguard_media_visibility"
+    namespace: "systemui"
+    description: "Binds Keyguard Media Controller Visibility to MediaContainerView"
+    bug: "298213983"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
index 535c2d3..e862f0c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
@@ -17,7 +17,6 @@
 
 import android.view.View
 import androidx.annotation.VisibleForTesting
-import java.util.Random
 
 /** Plays [TurbulenceNoiseView] in ease-in, main (no easing), and ease-out order. */
 class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) {
@@ -37,8 +36,6 @@
         }
     }
 
-    private val random = Random()
-
     /** Current state of the animation. */
     @VisibleForTesting
     var state: AnimationState = AnimationState.NOT_PLAYING
@@ -95,12 +92,7 @@
         }
         state = AnimationState.EASE_IN
 
-        // Add offset to avoid repetitive noise.
-        turbulenceNoiseView.playEaseIn(
-            offsetX = random.nextFloat(),
-            offsetY = random.nextFloat(),
-            this::playMainAnimation
-        )
+        turbulenceNoiseView.playEaseIn(this::playMainAnimation)
     }
 
     private fun playMainAnimation() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
index c59bc10..5e72e3b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
@@ -109,7 +109,7 @@
 
     /** Plays the turbulence noise with linear ease-in. */
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-    fun playEaseIn(offsetX: Float = 0f, offsetY: Float = 0f, onAnimationEnd: Runnable? = null) {
+    fun playEaseIn(onAnimationEnd: Runnable? = null) {
         if (noiseConfig == null) {
             return
         }
@@ -129,8 +129,8 @@
             val progress = updateListener.animatedValue as Float
 
             shader.setNoiseMove(
-                offsetX + initialX + timeInSec * config.noiseMoveSpeedX,
-                offsetY + initialY + timeInSec * config.noiseMoveSpeedY,
+                initialX + timeInSec * config.noiseMoveSpeedX,
+                initialY + timeInSec * config.noiseMoveSpeedY,
                 initialZ + timeInSec * config.noiseMoveSpeedZ
             )
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 8bd0d45..97d5b41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
@@ -55,7 +54,6 @@
     private val vibratorHelper: VibratorHelper,
     private val indicationController: KeyguardIndicationController,
     private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
-    private val alphaViewModel: AodAlphaViewModel,
 ) {
     /**
      * Renders a single lockscreen shortcut.
@@ -104,7 +102,6 @@
             content {
                 IndicationArea(
                     indicationAreaViewModel = indicationAreaViewModel,
-                    alphaViewModel = alphaViewModel,
                     indicationController = indicationController,
                 )
             }
@@ -183,7 +180,6 @@
     @Composable
     private fun IndicationArea(
         indicationAreaViewModel: KeyguardIndicationAreaViewModel,
-        alphaViewModel: AodAlphaViewModel,
         indicationController: KeyguardIndicationController,
         modifier: Modifier = Modifier,
     ) {
@@ -196,7 +192,6 @@
                     KeyguardIndicationAreaBinder.bind(
                         view = view,
                         viewModel = indicationAreaViewModel,
-                        aodAlphaViewModel = alphaViewModel,
                         indicationController = indicationController,
                     )
                 )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 4156d83..ce96d75 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -18,7 +18,9 @@
 package com.android.systemui.communal.domain.interactor
 
 import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
 import android.content.pm.UserInfo
+import android.os.UserHandle
 import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -51,6 +53,8 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
 import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
 import com.android.systemui.testKosmos
@@ -96,6 +100,7 @@
     private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
     private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
     private lateinit var sceneInteractor: SceneInteractor
+    private lateinit var userTracker: FakeUserTracker
 
     private lateinit var underTest: CommunalInteractor
 
@@ -113,6 +118,7 @@
         editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
         communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
         sceneInteractor = kosmos.sceneInteractor
+        userTracker = kosmos.fakeUserTracker
 
         whenever(mainUser.isMain).thenReturn(true)
         whenever(secondaryUser.isMain).thenReturn(false)
@@ -207,25 +213,19 @@
             keyguardRepository.setKeyguardOccluded(false)
             tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
-            // Widgets are available.
-            val widgets =
-                listOf(
-                    CommunalWidgetContentModel(
-                        appWidgetId = 0,
-                        priority = 30,
-                        providerInfo = mock(),
-                    ),
-                    CommunalWidgetContentModel(
-                        appWidgetId = 1,
-                        priority = 20,
-                        providerInfo = mock(),
-                    ),
-                    CommunalWidgetContentModel(
-                        appWidgetId = 2,
-                        priority = 10,
-                        providerInfo = mock(),
-                    ),
-                )
+            val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
+            userRepository.setUserInfos(userInfos)
+            userTracker.set(
+                userInfos = userInfos,
+                selectedUserIndex = 0,
+            )
+            runCurrent()
+
+            // Widgets available.
+            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+            val widgets = listOf(widget1, widget2, widget3)
             widgetRepository.setCommunalWidgets(widgets)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
@@ -752,6 +752,38 @@
             verify(editWidgetsActivityStarter).startActivity(widgetKey)
         }
 
+    @Test
+    fun filterWidgets_whenUserProfileRemoved() =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            // Only main user exists.
+            val userInfos = listOf(MAIN_USER_INFO)
+            userRepository.setUserInfos(userInfos)
+            userTracker.set(
+                userInfos = userInfos,
+                selectedUserIndex = 0,
+            )
+            runCurrent()
+
+            val widgetContent by collectLastValue(underTest.widgetContent)
+            // Given three widgets, and one of them is associated with pre-existing work profile.
+            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+            val widgets = listOf(widget1, widget2, widget3)
+            widgetRepository.setCommunalWidgets(widgets)
+
+            // One widget is filtered out and the remaining two link to main user id.
+            assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
+            widgetContent!!.forEachIndexed { _, model ->
+                assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id)
+            }
+        }
+
     private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
         val timer = mock(SmartspaceTarget::class.java)
         whenever(timer.smartspaceTargetId).thenReturn(id)
@@ -760,4 +792,17 @@
         whenever(timer.creationTimeMillis).thenReturn(timestamp)
         return timer
     }
+
+    private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
+        mock<CommunalWidgetContentModel> {
+            whenever(this.appWidgetId).thenReturn(appWidgetId)
+            val providerInfo = mock<AppWidgetProviderInfo>()
+            whenever(providerInfo.profile).thenReturn(UserHandle(userId))
+            whenever(this.providerInfo).thenReturn(providerInfo)
+        }
+
+    private companion object {
+        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+        val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 352bacc..5ee88cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.communal.view.viewmodel
 
 import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
+import android.content.pm.UserInfo
+import android.os.UserHandle
 import android.provider.Settings
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -39,6 +42,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
 import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
 import com.android.systemui.testKosmos
@@ -59,6 +63,7 @@
 class CommunalEditModeViewModelTest : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
     @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var providerInfo: AppWidgetProviderInfo
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -78,6 +83,11 @@
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         smartspaceRepository = kosmos.fakeSmartspaceRepository
         mediaRepository = kosmos.fakeCommunalMediaRepository
+        kosmos.fakeUserTracker.set(
+            userInfos = listOf(MAIN_USER_INFO),
+            selectedUserIndex = 0,
+        )
+        whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
 
         underTest =
             CommunalEditModeViewModel(
@@ -100,12 +110,12 @@
                     CommunalWidgetContentModel(
                         appWidgetId = 0,
                         priority = 30,
-                        providerInfo = mock(),
+                        providerInfo = providerInfo,
                     ),
                     CommunalWidgetContentModel(
                         appWidgetId = 1,
                         priority = 20,
-                        providerInfo = mock(),
+                        providerInfo = providerInfo,
                     ),
                 )
             widgetRepository.setCommunalWidgets(widgets)
@@ -156,12 +166,12 @@
                     CommunalWidgetContentModel(
                         appWidgetId = 0,
                         priority = 30,
-                        providerInfo = mock(),
+                        providerInfo = providerInfo,
                     ),
                     CommunalWidgetContentModel(
                         appWidgetId = 1,
                         priority = 20,
-                        providerInfo = mock(),
+                        providerInfo = providerInfo,
                     ),
                 )
             widgetRepository.setCommunalWidgets(widgets)
@@ -205,4 +215,8 @@
         underTest.onReorderWidgetCancel()
         verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
     }
+
+    private companion object {
+        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index cc322d0..1e523dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.communal.view.viewmodel
 
 import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
 import android.content.pm.UserInfo
+import android.os.UserHandle
 import android.provider.Settings
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -45,13 +47,13 @@
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
 import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -71,6 +73,7 @@
 class CommunalViewModelTest : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
     @Mock private lateinit var user: UserInfo
+    @Mock private lateinit var providerInfo: AppWidgetProviderInfo
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -98,6 +101,12 @@
         kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
+        kosmos.fakeUserTracker.set(
+            userInfos = listOf(MAIN_USER_INFO),
+            selectedUserIndex = 0,
+        )
+        whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
+
         underTest =
             CommunalViewModel(
                 testScope,
@@ -147,12 +156,12 @@
                     CommunalWidgetContentModel(
                         appWidgetId = 0,
                         priority = 30,
-                        providerInfo = mock(),
+                        providerInfo = providerInfo,
                     ),
                     CommunalWidgetContentModel(
                         appWidgetId = 1,
                         priority = 20,
-                        providerInfo = mock(),
+                        providerInfo = providerInfo,
                     ),
                 )
             widgetRepository.setCommunalWidgets(widgets)
@@ -225,4 +234,8 @@
         userRepository.setUserInfos(listOf(user))
         userRepository.setSelectedUserInfo(user)
     }
+
+    private companion object {
+        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 8488843..2c9d72c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.communal.widgets
 
+import android.appwidget.AppWidgetProviderInfo
 import android.content.pm.UserInfo
+import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
@@ -32,6 +34,7 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
@@ -65,7 +68,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+        kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO, USER_INFO_WORK))
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
@@ -76,6 +79,7 @@
             CommunalAppWidgetHostStartable(
                 appWidgetHost,
                 kosmos.communalInteractor,
+                kosmos.fakeUserTracker,
                 kosmos.applicationCoroutineScope,
                 kosmos.testDispatcher,
             )
@@ -170,6 +174,46 @@
             }
         }
 
+    @Test
+    fun removeWidgetsForDeletedProfile_whenCommunalIsAvailable() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is available and work profile is configured.
+                setCommunalAvailable(true)
+                kosmos.fakeUserTracker.set(
+                    userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK),
+                    selectedUserIndex = 0,
+                )
+                val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+                val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+                val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+                val widgets = listOf(widget1, widget2, widget3)
+                fakeCommunalWidgetRepository.setCommunalWidgets(widgets)
+
+                underTest.start()
+                runCurrent()
+
+                val communalWidgets by
+                    collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+                assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+
+                // Unlock the device and remove work profile.
+                fakeKeyguardRepository.setKeyguardShowing(false)
+                kosmos.fakeUserTracker.set(
+                    userInfos = listOf(MAIN_USER_INFO),
+                    selectedUserIndex = 0,
+                )
+                runCurrent()
+
+                // Communal becomes available.
+                fakeKeyguardRepository.setKeyguardShowing(true)
+                runCurrent()
+
+                // Widget created for work profile is removed.
+                assertThat(communalWidgets).containsExactly(widget2, widget3)
+            }
+        }
+
     private suspend fun setCommunalAvailable(available: Boolean) =
         with(kosmos) {
             fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
@@ -179,7 +223,16 @@
             fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id)
         }
 
+    private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
+        mock<CommunalWidgetContentModel> {
+            whenever(this.appWidgetId).thenReturn(appWidgetId)
+            val providerInfo = mock<AppWidgetProviderInfo>()
+            whenever(providerInfo.profile).thenReturn(UserHandle(userId))
+            whenever(this.providerInfo).thenReturn(providerInfo)
+        }
+
     private companion object {
         val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+        val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
     }
 }
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index ae81d29..2616e8a 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -72,7 +72,7 @@
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
-            <TextView
+            <Button
                 android:id="@android:id/button2"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
@@ -83,7 +83,7 @@
                 android:layout_width="0dp"
                 android:layout_height="match_parent"
                 android:layout_weight="1"/>
-            <TextView
+            <Button
                 android:id="@android:id/button1"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 495f20f2..53ad344 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3129,6 +3129,8 @@
     <!-- [CHAR LIMIT=25] Long label used by Note Task Shortcut -->
     <string name="note_task_shortcut_long_label">Note-taking, <xliff:g id="note_taking_app" example="Note-taking App">%1$s</xliff:g></string>
 
+    <!-- [CHAR LIMIT=NONE] Output switch chip text during broadcasting -->
+    <string name="audio_sharing_description">Sharing audio</string>
     <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
     <string name="broadcasting_description_is_broadcasting">Broadcasting</string>
     <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
index 00bbb20..6af0fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
@@ -40,6 +40,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.media.MediaOutputConstants;
 import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.controls.util.MediaDataUtils;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.res.R;
@@ -74,7 +75,7 @@
     private final SystemUIDialog.Factory mSystemUIDialogFactory;
     private final String mCurrentBroadcastApp;
     private final String mOutputPackageName;
-    private final Executor mExecutor;
+    private final Executor mBgExecutor;
     private boolean mShouldLaunchLeBroadcastDialog;
     private Button mSwitchBroadcast;
 
@@ -159,7 +160,7 @@
             MediaOutputDialogFactory mediaOutputDialogFactory,
             @Nullable LocalBluetoothManager localBluetoothManager,
             UiEventLogger uiEventLogger,
-            Executor executor,
+            @Background Executor bgExecutor,
             BroadcastSender broadcastSender,
             SystemUIDialog.Factory systemUIDialogFactory,
             @Assisted(CURRENT_BROADCAST_APP) String currentBroadcastApp,
@@ -171,7 +172,7 @@
         mCurrentBroadcastApp = currentBroadcastApp;
         mOutputPackageName = outputPkgName;
         mUiEventLogger = uiEventLogger;
-        mExecutor = executor;
+        mBgExecutor = bgExecutor;
         mBroadcastSender = broadcastSender;
 
         if (DEBUG) {
@@ -187,7 +188,7 @@
     @Override
     public void onStart(SystemUIDialog dialog) {
         mDialogs.add(dialog);
-        registerBroadcastCallBack(mExecutor, mBroadcastCallback);
+        registerBroadcastCallBack(mBgExecutor, mBroadcastCallback);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 2af49cf..b269967 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -143,7 +143,7 @@
         mTextPreview.getViewTreeObserver().addOnPreDrawListener(() -> {
             int availableHeight = mTextPreview.getHeight()
                     - (mTextPreview.getPaddingTop() + mTextPreview.getPaddingBottom());
-            mTextPreview.setMaxLines(availableHeight / mTextPreview.getLineHeight());
+            mTextPreview.setMaxLines(Math.max(availableHeight / mTextPreview.getLineHeight(), 1));
             return true;
         });
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index d0044a4..5397837 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
 import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -45,6 +46,7 @@
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
 import com.android.systemui.util.kotlin.BooleanFlowOperators.and
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -59,6 +61,7 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
@@ -82,6 +85,7 @@
     communalSettingsInteractor: CommunalSettingsInteractor,
     private val appWidgetHost: CommunalAppWidgetHost,
     private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
+    private val userTracker: UserTracker,
     sceneInteractor: SceneInteractor,
     sceneContainerFlags: SceneContainerFlags,
     @CommunalLog logBuffer: LogBuffer,
@@ -262,10 +266,16 @@
     fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) =
         widgetRepository.updateWidgetOrder(widgetIdToPriorityMap)
 
+    /** All widgets present in db. */
+    val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
+        isCommunalAvailable.flatMapLatest { available ->
+            if (!available) emptyFlow() else widgetRepository.communalWidgets
+        }
+
     /** A list of widget content to be displayed in the communal hub. */
     val widgetContent: Flow<List<CommunalContentModel.Widget>> =
         widgetRepository.communalWidgets.map { widgets ->
-            widgets.map Widget@{ widget ->
+            filterWidgetsByExistingUsers(widgets).map Widget@{ widget ->
                 return@Widget CommunalContentModel.Widget(
                     appWidgetId = widget.appWidgetId,
                     providerInfo = widget.providerInfo,
@@ -345,6 +355,19 @@
             return@combine ongoingContent
         }
 
+    /**
+     * Filter and retain widgets associated with an existing user, safeguarding against displaying
+     * stale data following user deletion.
+     */
+    private fun filterWidgetsByExistingUsers(
+        list: List<CommunalWidgetContentModel>,
+    ): List<CommunalWidgetContentModel> {
+        val currentUserIds = userTracker.userProfiles.map { it.id }.toSet()
+        return list.filter { widget ->
+            currentUserIds.contains(widget.providerInfo.profile?.identifier)
+        }
+    }
+
     companion object {
         /**
          * The user activity timeout which should be used when the communal hub is opened. A value
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 4ddd768..8390d62 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -18,11 +18,14 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.kotlin.BooleanFlowOperators.or
 import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -37,6 +40,7 @@
 constructor(
     private val appWidgetHost: CommunalAppWidgetHost,
     private val communalInteractor: CommunalInteractor,
+    private val userTracker: UserTracker,
     @Background private val bgScope: CoroutineScope,
     @Main private val uiDispatcher: CoroutineDispatcher
 ) : CoreStartable {
@@ -47,6 +51,14 @@
             .pairwise(false)
             .filter { (previous, new) -> previous != new }
             .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
+            .sample(communalInteractor.communalWidgets, ::Pair)
+            .onEach { (withPrev, widgets) ->
+                val (_, isActive) = withPrev
+                // The validation is performed once the hub becomes active.
+                if (isActive) {
+                    validateWidgetsAndDeleteOrphaned(widgets)
+                }
+            }
             .launchIn(bgScope)
 
         appWidgetHost.appWidgetIdToRemove
@@ -63,4 +75,15 @@
                 appWidgetHost.stopListening()
             }
         }
+
+    /**
+     * Ensure the existence of all associated users for widgets, and remove widgets belonging to
+     * users who have been deleted.
+     */
+    private fun validateWidgetsAndDeleteOrphaned(widgets: List<CommunalWidgetContentModel>) {
+        val currentUserIds = userTracker.userProfiles.map { it.id }.toSet()
+        widgets
+            .filter { widget -> !currentUserIds.contains(widget.providerInfo.profile?.identifier) }
+            .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 0f038e1..bc07b95 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.settings.ControlsSettingsRepository
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.VibratorHelper
@@ -47,16 +48,16 @@
 
 @SysUISingleton
 class ControlActionCoordinatorImpl @Inject constructor(
-        private val context: Context,
-        private val bgExecutor: DelayableExecutor,
-        @Main private val uiExecutor: DelayableExecutor,
-        private val activityStarter: ActivityStarter,
-        private val broadcastSender: BroadcastSender,
-        private val keyguardStateController: KeyguardStateController,
-        private val taskViewFactory: Optional<TaskViewFactory>,
-        private val controlsMetricsLogger: ControlsMetricsLogger,
-        private val vibrator: VibratorHelper,
-        private val controlsSettingsRepository: ControlsSettingsRepository,
+    private val context: Context,
+    @Background private val bgExecutor: DelayableExecutor,
+    @Main private val uiExecutor: DelayableExecutor,
+    private val activityStarter: ActivityStarter,
+    private val broadcastSender: BroadcastSender,
+    private val keyguardStateController: KeyguardStateController,
+    private val taskViewFactory: Optional<TaskViewFactory>,
+    private val controlsMetricsLogger: ControlsMetricsLogger,
+    private val vibrator: VibratorHelper,
+    private val controlsSettingsRepository: ControlsSettingsRepository,
 ) : ControlActionCoordinator {
     private var dialog: Dialog? = null
     private var pendingAction: Action? = null
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e893177..1157d97 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -126,6 +126,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.PolicyModule;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
@@ -358,6 +359,7 @@
             VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
+            SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
             CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
@@ -376,6 +378,7 @@
                 visualInterruptionDecisionProvider,
                 zenModeController,
                 notifUserManager,
+                sensitiveNotificationProtectionController,
                 notifCollection,
                 notifPipeline,
                 sysUiState,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e35c5a6..301942f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -170,7 +170,6 @@
             KeyguardIndicationAreaBinder.bind(
                 notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area),
                 keyguardIndicationAreaViewModel,
-                aodAlphaViewModel,
                 indicationController,
             )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 7c1368a..841f52d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,7 +23,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
+import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
@@ -51,7 +51,6 @@
     fun bind(
         view: ViewGroup,
         viewModel: KeyguardIndicationAreaViewModel,
-        aodAlphaViewModel: AodAlphaViewModel,
         indicationController: KeyguardIndicationController,
     ): DisposableHandle {
         indicationController.setIndicationArea(view)
@@ -68,30 +67,10 @@
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
                     launch {
-                        if (keyguardBottomAreaRefactor()) {
-                            aodAlphaViewModel.alpha.collect { alpha ->
-                                view.apply {
-                                    this.importantForAccessibility =
-                                        if (alpha == 0f) {
-                                            View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                                        } else {
-                                            View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                                        }
-                                    this.alpha = alpha
-                                }
-                            }
-                        } else {
-                            viewModel.alpha.collect { alpha ->
-                                view.apply {
-                                    this.importantForAccessibility =
-                                        if (alpha == 0f) {
-                                            View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                                        } else {
-                                            View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                                        }
-                                    this.alpha = alpha
-                                }
-                            }
+                        // Do not independently apply alpha, as [KeyguardRootViewModel] should work
+                        // for this and all its children
+                        if (!(migrateClocksToBlueprint() || keyguardBottomAreaRefactor())) {
+                            viewModel.alpha.collect { alpha -> view.alpha = alpha }
                         }
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
index 78099d9..a53c6d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
@@ -50,6 +50,15 @@
         )
     }
 
+    override fun setAlpha(alpha: Float) {
+        super.setAlpha(alpha)
+
+        if (alpha == 0f) {
+            importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+        } else {
+            importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+        }
+    }
     private fun indicationTopRow(): KeyguardIndicationTextView {
         return KeyguardIndicationTextView(context, attrs).apply {
             id = R.id.keyguard_indication_text
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index ea05c1d..3361343 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -37,7 +36,6 @@
 constructor(
     private val context: Context,
     private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
-    private val aodAlphaViewModel: AodAlphaViewModel,
     private val indicationController: KeyguardIndicationController,
 ) : KeyguardSection() {
     private val indicationAreaViewId = R.id.keyguard_indication_area
@@ -56,7 +54,6 @@
                 KeyguardIndicationAreaBinder.bind(
                     constraintLayout.requireViewById(R.id.keyguard_indication_area),
                     keyguardIndicationAreaViewModel,
-                    aodAlphaViewModel,
                     indicationController,
                 )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 42d68ba..f4d70a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -30,6 +30,8 @@
 import androidx.annotation.WorkerThread
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
 import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags.enableLeAudioSharing
+import com.android.settingslib.flags.Flags.legacyLeAudioSharing
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.media.PhoneMediaDevice
@@ -332,14 +334,28 @@
         @WorkerThread
         private fun updateCurrent() {
             if (isLeAudioBroadcastEnabled()) {
-                current =
-                    MediaDeviceData(
-                        /* enabled */ true,
-                        /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
-                        /* name */ broadcastDescription,
-                        /* intent */ null,
-                        /* showBroadcastButton */ showBroadcastButton = true
-                    )
+                if (enableLeAudioSharing()) {
+                    current =
+                        MediaDeviceData(
+                            enabled = false,
+                            icon =
+                                context.getDrawable(
+                                    com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
+                                ),
+                            name = context.getString(R.string.audio_sharing_description),
+                            intent = null,
+                            showBroadcastButton = false
+                        )
+                } else {
+                    current =
+                        MediaDeviceData(
+                            /* enabled */ true,
+                            /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
+                            /* name */ broadcastDescription,
+                            /* intent */ null,
+                            /* showBroadcastButton */ showBroadcastButton = true
+                        )
+                }
             } else {
                 val aboutToConnect = aboutToConnectDeviceOverride
                 if (
@@ -420,6 +436,7 @@
 
         @WorkerThread
         private fun isLeAudioBroadcastEnabled(): Boolean {
+            if (!enableLeAudioSharing() && !legacyLeAudioSharing()) return false
             val localBluetoothManager = localBluetoothManager.get()
             if (localBluetoothManager != null) {
                 val profileManager = localBluetoothManager.profileManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index 9206af2..ba7d410 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -300,10 +300,17 @@
     private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
         val currentMediaContainer = view ?: return
 
-        val previousVisibility = currentMediaContainer.visibility
-        currentMediaContainer.visibility = newVisibility
-        if (previousVisibility != newVisibility && currentMediaContainer is MediaContainerView) {
-            visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
+        val isVisible = newVisibility == View.VISIBLE
+
+        if (currentMediaContainer is MediaContainerView) {
+            val previousVisibility = currentMediaContainer.visibility
+
+            currentMediaContainer.setKeyguardVisibility(isVisible)
+            if (previousVisibility != newVisibility) {
+                visibilityChangedListener?.invoke(isVisible)
+            }
+        } else {
+            currentMediaContainer.visibility = newVisibility
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index e8ad4d3..4e940f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -260,7 +260,6 @@
     private TurbulenceNoiseController mTurbulenceNoiseController;
     private LoadingEffect mLoadingEffect;
     private final GlobalSettings mGlobalSettings;
-    private final Random mRandom = new Random();
     private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig;
     private boolean mWasPlaying = false;
     private boolean mButtonClicked = false;
@@ -1294,13 +1293,14 @@
                 mMediaViewHolder.getTurbulenceNoiseView();
         int width = targetView.getWidth();
         int height = targetView.getHeight();
+        Random random = new Random();
 
         return new TurbulenceNoiseAnimationConfig(
                 /* gridCount= */ 2.14f,
                 TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
-                /* noiseOffsetX= */ mRandom.nextFloat(),
-                /* noiseOffsetY= */ mRandom.nextFloat(),
-                /* noiseOffsetZ= */ mRandom.nextFloat(),
+                /* noiseOffsetX= */ random.nextFloat(),
+                /* noiseOffsetY= */ random.nextFloat(),
+                /* noiseOffsetZ= */ random.nextFloat(),
                 /* noiseMoveSpeedX= */ 0.42f,
                 /* noiseMoveSpeedY= */ 0f,
                 TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 416eae1..4f062af 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -72,7 +72,7 @@
         context: Context,
         logger: MediaTttReceiverLogger,
         windowManager: WindowManager,
-        mainExecutor: DelayableExecutor,
+        @Main mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
         configurationController: ConfigurationController,
         dumpManager: DumpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 72a5c46..c1b2037 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -84,6 +84,10 @@
      */
     val qsHeight: Int
 
+    /** Compatibility for use by LockscreenShadeTransitionController. Matches default from [QS] */
+    val isQsFullyCollapsed: Boolean
+        get() = true
+
     sealed interface State {
 
         val isVisible: Boolean
@@ -165,6 +169,10 @@
     override val qsHeight: Int
         get() = qsImpl.value?.qsHeight ?: 0
 
+    // If value is null, there's no QS and therefore it's fully collapsed.
+    override val isQsFullyCollapsed: Boolean
+        get() = qsImpl.value?.isFullyCollapsed ?: true
+
     // Same config changes as in FragmentHostManager
     private val interestingChanges =
         InterestingConfigChanges(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index bee3152..fb5339d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -31,10 +31,13 @@
 import android.view.WindowManagerGlobal
 import com.android.app.tracing.coroutines.launch
 import com.android.internal.infra.ServiceConnector
+import com.android.systemui.Flags.screenshotActionDismissSystemWindows
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.statusbar.phone.CentralSurfaces
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineDispatcher
@@ -46,9 +49,11 @@
 @Inject
 constructor(
     private val context: Context,
+    private val activityManagerWrapper: ActivityManagerWrapper,
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val displayTracker: DisplayTracker,
+    private val keyguardController: ScreenshotKeyguardController,
 ) {
     /**
      * Execute the given intent with startActivity while performing operations for screenshot action
@@ -74,7 +79,14 @@
         user: UserHandle,
         overrideTransition: Boolean,
     ) {
-        dismissKeyguard()
+        if (screenshotActionDismissSystemWindows()) {
+            keyguardController.dismiss()
+            activityManagerWrapper.closeSystemWindows(
+                CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
+            )
+        } else {
+            dismissKeyguard()
+        }
 
         if (user == myUserHandle()) {
             withContext(mainDispatcher) { context.startActivity(intent, options) }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
new file mode 100644
index 0000000..7696bbe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.Context
+import android.content.Intent
+import com.android.internal.infra.ServiceConnector
+import javax.inject.Inject
+import kotlinx.coroutines.CompletableDeferred
+
+open class ScreenshotKeyguardController @Inject constructor(context: Context) {
+    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
+        ServiceConnector.Impl(
+            context,
+            Intent(context, ScreenshotProxyService::class.java),
+            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
+            context.userId,
+            IScreenshotProxy.Stub::asInterface
+        )
+
+    suspend fun dismiss() {
+        val completion = CompletableDeferred<Unit>()
+        val onDoneBinder =
+            object : IOnDoneCallback.Stub() {
+                override fun onDone(success: Boolean) {
+                    completion.complete(Unit)
+                }
+            }
+        proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
+        completion.await()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index 86f6523..d5ab306 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -17,15 +17,16 @@
 
 import android.content.Intent
 import android.os.IBinder
+import android.os.RemoteException
 import android.util.Log
 import androidx.lifecycle.LifecycleService
 import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launch
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.shade.ShadeExpansionStateManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import com.android.app.tracing.coroutines.launch
 import kotlinx.coroutines.withContext
 
 /** Provides state from the main SystemUI process on behalf of the Screenshot process. */
@@ -56,7 +57,13 @@
     private suspend fun executeAfterDismissing(callback: IOnDoneCallback) =
         withContext(mMainDispatcher) {
             activityStarter.executeRunnableDismissingKeyguard(
-                Runnable { callback.onDone(true) },
+                {
+                    try {
+                        callback.onDone(true)
+                    } catch (e: RemoteException) {
+                        Log.w(TAG, "Failed to complete callback transaction", e)
+                    }
+                },
                 null,
                 true /* dismissShade */,
                 true /* afterKeyguardGone */,
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index f2fa0ef..125f7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -121,7 +121,6 @@
     @GuardedBy("callbacks")
     private val callbacks: MutableList<DataItem> = ArrayList()
 
-    private var beforeUserSwitchingJob: Job? = null
     private var userSwitchingJob: Job? = null
     private var afterUserSwitchingJob: Job? = null
 
@@ -194,14 +193,7 @@
     private fun registerUserSwitchObserver() {
         iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
             override fun onBeforeUserSwitching(newUserId: Int) {
-                if (isBackgroundUserSwitchEnabled) {
-                    beforeUserSwitchingJob?.cancel()
-                    beforeUserSwitchingJob = appScope.launch(backgroundContext) {
-                        handleBeforeUserSwitching(newUserId)
-                    }
-                } else {
-                    handleBeforeUserSwitching(newUserId)
-                }
+                handleBeforeUserSwitching(newUserId)
             }
 
             override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -233,15 +225,24 @@
 
     @WorkerThread
     protected open fun handleBeforeUserSwitching(newUserId: Int) {
-        Assert.isNotMainThread()
         setUserIdInternal(newUserId)
 
         val list = synchronized(callbacks) {
             callbacks.toList()
         }
+        val latch = CountDownLatch(list.size)
         list.forEach {
-            it.callback.get()?.onBeforeUserSwitching(newUserId)
+            val callback = it.callback.get()
+            if (callback != null) {
+                it.executor.execute {
+                    callback.onBeforeUserSwitching(newUserId)
+                    latch.countDown()
+                }
+            } else {
+                latch.countDown()
+            }
         }
+        latch.await()
     }
 
     @WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
index 4d0552e..adca3f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
@@ -21,9 +21,9 @@
 import android.util.MathUtils
 import androidx.annotation.FloatRange
 import androidx.annotation.Px
-import com.android.systemui.res.R
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QS
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import dagger.assisted.Assisted
@@ -38,7 +38,7 @@
     context: Context,
     configurationController: ConfigurationController,
     dumpManager: DumpManager,
-    @Assisted private val qsProvider: () -> QS,
+    @Assisted private val qsProvider: () -> QS?,
     splitShadeStateController: SplitShadeStateController
 ) :
     AbstractLockscreenShadeTransitionController(
@@ -48,7 +48,7 @@
         splitShadeStateController
     ) {
 
-    private val qs: QS
+    private val qs: QS?
         get() = qsProvider()
 
     /**
@@ -135,7 +135,7 @@
                 /* amount= */ MathUtils.saturate(qsDragDownAmount / qsSquishTransitionDistance)
             )
         isTransitioningToFullShade = dragDownAmount > 0.0f
-        qs.setTransitionToFullShadeProgress(
+        qs?.setTransitionToFullShadeProgress(
             isTransitioningToFullShade,
             qsTransitionFraction,
             qsSquishTransitionFraction
@@ -163,6 +163,6 @@
 
     @AssistedFactory
     fun interface Factory {
-        fun create(qsProvider: () -> QS): LockscreenShadeQsTransitionController
+        fun create(qsProvider: () -> QS?): LockscreenShadeQsTransitionController
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index a59d753..4ee8349 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeLockscreenInteractor
 import com.android.systemui.shade.data.repository.ShadeRepository
@@ -84,6 +85,7 @@
     private val splitShadeStateController: SplitShadeStateController,
     private val shadeLockscreenInteractorLazy: Lazy<ShadeLockscreenInteractor>,
     naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
+    private val lazyQSSceneAdapter: Lazy<QSSceneAdapter>,
 ) : Dumpable {
     private var pulseHeight: Float = 0f
 
@@ -93,7 +95,11 @@
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
     lateinit var centralSurfaces: CentralSurfaces
-    lateinit var qS: QS
+
+    // When in scene container mode, this will be null. In that case, we use the adapter if needed
+    var qS: QS? = null
+    private val isQsFullyCollapsed: Boolean
+        get() = qS?.isFullyCollapsed ?: lazyQSSceneAdapter.get().isQsFullyCollapsed
 
     /** A handler that handles the next keyguard dismiss animation. */
     private var animationHandlerOnKeyguardDismiss: ((Long) -> Unit)? = null
@@ -286,7 +292,8 @@
     /** @return true if the interaction is accepted, false if it should be cancelled */
     internal fun canDragDown(): Boolean {
         return (statusBarStateController.state == StatusBarState.KEYGUARD ||
-            nsslController.isInLockedDownShade()) && (qS.isFullyCollapsed || useSplitShade)
+            nsslController.isInLockedDownShade()) &&
+                (isQsFullyCollapsed || useSplitShade)
     }
 
     /** Called by the touch helper when when a gesture has completed all the way and released. */
@@ -410,7 +417,7 @@
         get() =
             (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
                 !keyguardBypassController.bypassEnabled &&
-                (qS.isFullyCollapsed || useSplitShade))
+                (isQsFullyCollapsed || useSplitShade))
 
     /** The amount in pixels that the user has dragged down. */
     internal var dragDownAmount = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
index e47c914..612a365 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -27,7 +27,7 @@
     private val context: Context,
     private val scrimController: ScrimController,
     private val statusBarStateController: SysuiStatusBarStateController,
-    @Assisted private val qSProvider: () -> QS,
+    @Assisted private val qSProvider: () -> QS?,
     @Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
 ) : LockScreenShadeOverScroller {
 
@@ -37,7 +37,7 @@
     private var maxOverScrollAmount = 0
     private var previousOverscrollAmount = 0
 
-    private val qS: QS
+    private val qS: QS?
         get() = qSProvider()
 
     private val nsslController: NotificationStackScrollLayoutController
@@ -90,7 +90,7 @@
     }
 
     private fun applyOverscroll(overscrollAmount: Int) {
-        qS.setOverScrollAmount(overscrollAmount)
+        qS?.setOverScrollAmount(overscrollAmount)
         scrimController.setNotificationsOverScrollAmount(overscrollAmount)
         nsslController.setOverScrollAmount(overscrollAmount)
     }
@@ -109,7 +109,7 @@
         val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
         animator.addUpdateListener {
             val overScrollAmount = it.animatedValue as Int
-            qS.setOverScrollAmount(overScrollAmount)
+            qS?.setOverScrollAmount(overScrollAmount)
             scrimController.setNotificationsOverScrollAmount(overScrollAmount)
             nsslController.setOverScrollAmount(overScrollAmount)
         }
@@ -143,7 +143,7 @@
     @AssistedFactory
     fun interface Factory {
         fun create(
-            qSProvider: () -> QS,
+            qSProvider: () -> QS?,
             nsslControllerProvider: () -> NotificationStackScrollLayoutController
         ): SplitShadeLockScreenOverScroller
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 8189fe0..dfe6cd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -25,6 +25,7 @@
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -92,7 +93,7 @@
 
     @Inject
     public VisualStabilityCoordinator(
-            DelayableExecutor delayableExecutor,
+            @Background DelayableExecutor delayableExecutor,
             DumpManager dumpManager,
             HeadsUpManager headsUpManager,
             ShadeAnimationInteractor shadeAnimationInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 76e5fd3..a5f42bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -45,10 +45,12 @@
  * icons and keeping the icon assets themselves up to date as notifications change.
  *
  * TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry.
- *  Long-term, it should probably live somewhere in the content inflation pipeline.
+ *   Long-term, it should probably live somewhere in the content inflation pipeline.
  */
 @SysUISingleton
-class IconManager @Inject constructor(
+class IconManager
+@Inject
+constructor(
     private val notifCollection: CommonNotifCollection,
     private val launcherApps: LauncherApps,
     private val iconBuilder: IconBuilder
@@ -59,30 +61,30 @@
         notifCollection.addCollectionListener(entryListener)
     }
 
-    private val entryListener = object : NotifCollectionListener {
-        override fun onEntryInit(entry: NotificationEntry) {
-            entry.addOnSensitivityChangedListener(sensitivityListener)
+    private val entryListener =
+        object : NotifCollectionListener {
+            override fun onEntryInit(entry: NotificationEntry) {
+                entry.addOnSensitivityChangedListener(sensitivityListener)
+            }
+
+            override fun onEntryCleanUp(entry: NotificationEntry) {
+                entry.removeOnSensitivityChangedListener(sensitivityListener)
+            }
+
+            override fun onRankingApplied() {
+                // rankings affect whether a conversation is important, which can change the icons
+                recalculateForImportantConversationChange()
+            }
         }
 
-        override fun onEntryCleanUp(entry: NotificationEntry) {
-            entry.removeOnSensitivityChangedListener(sensitivityListener)
-        }
-
-        override fun onRankingApplied() {
-            // rankings affect whether a conversation is important, which can change the icons
-            recalculateForImportantConversationChange()
-        }
-    }
-
-    private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener {
-        entry -> updateIconsSafe(entry)
-    }
+    private val sensitivityListener =
+        NotificationEntry.OnSensitivityChangedListener { entry -> updateIconsSafe(entry) }
 
     private fun recalculateForImportantConversationChange() {
         for (entry in notifCollection.allNotifs) {
             val isImportant = isImportantConversation(entry)
-            if (entry.icons.areIconsAvailable &&
-                isImportant != entry.icons.isImportantConversation
+            if (
+                entry.icons.areIconsAvailable && isImportant != entry.icons.isImportantConversation
             ) {
                 updateIconsSafe(entry)
             }
@@ -97,34 +99,35 @@
      * @throws InflationException Exception if required icons are not valid or specified
      */
     @Throws(InflationException::class)
-    fun createIcons(entry: NotificationEntry) = traceSection("IconManager.createIcons") {
-        // Construct the status bar icon view.
-        val sbIcon = iconBuilder.createIconView(entry)
-        sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+    fun createIcons(entry: NotificationEntry) =
+        traceSection("IconManager.createIcons") {
+            // Construct the status bar icon view.
+            val sbIcon = iconBuilder.createIconView(entry)
+            sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
 
-        // Construct the shelf icon view.
-        val shelfIcon = iconBuilder.createIconView(entry)
-        shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
-        shelfIcon.visibility = View.INVISIBLE
+            // Construct the shelf icon view.
+            val shelfIcon = iconBuilder.createIconView(entry)
+            shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+            shelfIcon.visibility = View.INVISIBLE
 
-        // Construct the aod icon view.
-        val aodIcon = iconBuilder.createIconView(entry)
-        aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
-        aodIcon.setIncreasedSize(true)
+            // Construct the aod icon view.
+            val aodIcon = iconBuilder.createIconView(entry)
+            aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+            aodIcon.setIncreasedSize(true)
 
-        // Set the icon views' icons
-        val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+            // Set the icon views' icons
+            val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
 
-        try {
-            setIcon(entry, normalIconDescriptor, sbIcon)
-            setIcon(entry, sensitiveIconDescriptor, shelfIcon)
-            setIcon(entry, sensitiveIconDescriptor, aodIcon)
-            entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
-        } catch (e: InflationException) {
-            entry.icons = IconPack.buildEmptyPack(entry.icons)
-            throw e
+            try {
+                setIcon(entry, normalIconDescriptor, sbIcon)
+                setIcon(entry, sensitiveIconDescriptor, shelfIcon)
+                setIcon(entry, sensitiveIconDescriptor, aodIcon)
+                entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
+            } catch (e: InflationException) {
+                entry.icons = IconPack.buildEmptyPack(entry.icons)
+                throw e
+            }
         }
-    }
 
     /**
      * Update the notification icons.
@@ -133,33 +136,33 @@
      * @throws InflationException Exception if required icons are not valid or specified
      */
     @Throws(InflationException::class)
-    fun updateIcons(entry: NotificationEntry) = traceSection("IconManager.updateIcons") {
-        if (!entry.icons.areIconsAvailable) {
-            return@traceSection
-        }
-        entry.icons.smallIconDescriptor = null
-        entry.icons.peopleAvatarDescriptor = null
+    fun updateIcons(entry: NotificationEntry) =
+        traceSection("IconManager.updateIcons") {
+            if (!entry.icons.areIconsAvailable) {
+                return@traceSection
+            }
+            entry.icons.smallIconDescriptor = null
+            entry.icons.peopleAvatarDescriptor = null
 
-        val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
-        val notificationContentDescription = entry.sbn.notification?.let {
-            iconBuilder.getIconContentDescription(it)
-        }
+            val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+            val notificationContentDescription =
+                entry.sbn.notification?.let { iconBuilder.getIconContentDescription(it) }
 
-        entry.icons.statusBarIcon?.let {
-            it.setNotification(entry.sbn, notificationContentDescription)
-            setIcon(entry, normalIconDescriptor, it)
-        }
+            entry.icons.statusBarIcon?.let {
+                it.setNotification(entry.sbn, notificationContentDescription)
+                setIcon(entry, normalIconDescriptor, it)
+            }
 
-        entry.icons.shelfIcon?.let {
-            it.setNotification(entry.sbn, notificationContentDescription)
-            setIcon(entry, normalIconDescriptor, it)
-        }
+            entry.icons.shelfIcon?.let {
+                it.setNotification(entry.sbn, notificationContentDescription)
+                setIcon(entry, sensitiveIconDescriptor, it)
+            }
 
-        entry.icons.aodIcon?.let {
-            it.setNotification(entry.sbn, notificationContentDescription)
-            setIcon(entry, sensitiveIconDescriptor, it)
+            entry.icons.aodIcon?.let {
+                it.setNotification(entry.sbn, notificationContentDescription)
+                setIcon(entry, sensitiveIconDescriptor, it)
+            }
         }
-    }
 
     private fun updateIconsSafe(entry: NotificationEntry) {
         try {
@@ -173,11 +176,12 @@
     @Throws(InflationException::class)
     private fun getIconDescriptors(entry: NotificationEntry): Pair<StatusBarIcon, StatusBarIcon> {
         val iconDescriptor = getIconDescriptor(entry, redact = false)
-        val sensitiveDescriptor = if (entry.isSensitive) {
-            getIconDescriptor(entry, redact = true)
-        } else {
-            iconDescriptor
-        }
+        val sensitiveDescriptor =
+            if (entry.isSensitive) {
+                getIconDescriptor(entry, redact = true)
+            } else {
+                iconDescriptor
+            }
         return Pair(iconDescriptor, sensitiveDescriptor)
     }
 
@@ -197,14 +201,15 @@
         }
 
         val icon =
-                (if (showPeopleAvatar) {
-                    createPeopleAvatar(entry)
-                } else {
-                    n.smallIcon
-                }) ?: throw InflationException(
-                        "No icon in notification from " + entry.sbn.packageName)
+            (if (showPeopleAvatar) {
+                createPeopleAvatar(entry)
+            } else {
+                n.smallIcon
+            })
+                ?: throw InflationException("No icon in notification from " + entry.sbn.packageName)
 
-        val ic = StatusBarIcon(
+        val ic =
+            StatusBarIcon(
                 entry.sbn.user,
                 entry.sbn.packageName,
                 icon,
@@ -282,8 +287,8 @@
 
     /**
      * Determines if this icon shows a conversation based on the sensitivity of the icon, its
-     * context and the user's indicated sensitivity preference. If we're using a fall back icon
-     * of the small icon, we don't consider this to be showing a conversation
+     * context and the user's indicated sensitivity preference. If we're using a fall back icon of
+     * the small icon, we don't consider this to be showing a conversation
      *
      * @param iconView The icon that shows the conversation.
      */
@@ -293,19 +298,20 @@
         iconDescriptor: StatusBarIcon
     ): Boolean {
         val usedInSensitiveContext =
-                iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
+            iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
         val isSmallIcon = iconDescriptor.icon.equals(entry.sbn.notification.smallIcon)
-        return isImportantConversation(entry) && !isSmallIcon &&
-                (!usedInSensitiveContext || !entry.isSensitive)
+        return isImportantConversation(entry) &&
+            !isSmallIcon &&
+            (!usedInSensitiveContext || !entry.isSensitive)
     }
 
     private fun isImportantConversation(entry: NotificationEntry): Boolean {
         // Also verify that the Notification is MessagingStyle, since we're going to access
         // MessagingStyle-specific data (EXTRA_MESSAGES, EXTRA_MESSAGING_PERSON).
         return entry.ranking.channel != null &&
-                entry.ranking.channel.isImportantConversation &&
-                entry.sbn.notification.isStyle(MessagingStyle::class.java) &&
-                entry.key !in unimportantConversationKeys
+            entry.ranking.channel.isImportantConversation &&
+            entry.sbn.notification.isStyle(MessagingStyle::class.java) &&
+            entry.key !in unimportantConversationKeys
     }
 
     override fun setUnimportantConversations(keys: Collection<String>) {
@@ -323,8 +329,8 @@
 interface ConversationIconManager {
     /**
      * Sets the complete current set of notification keys which should (for the purposes of icon
-     * presentation) be considered unimportant.  This tells the icon manager to remove the avatar
-     * of a group from which the priority notification has been removed.
+     * presentation) be considered unimportant. This tells the icon manager to remove the avatar of
+     * a group from which the priority notification has been removed.
      */
     fun setUnimportantConversations(keys: Collection<String>)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index bae5baa..5551ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -22,6 +22,8 @@
 import android.graphics.Path
 import android.graphics.RectF
 import android.util.AttributeSet
+import android.util.Log
+import com.android.systemui.Flags
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.row.ExpandableView
 
@@ -87,4 +89,63 @@
     ) {
         // No animation, it doesn't need it, this would be local
     }
+
+    override fun setVisibility(visibility: Int) {
+        if (Flags.bindKeyguardMediaVisibility()) {
+            if (isVisibilityValid(visibility)) {
+                super.setVisibility(visibility)
+            }
+        } else {
+            super.setVisibility(visibility)
+        }
+
+        assertMediaContainerVisibility(visibility)
+    }
+
+    /**
+     * visibility should be aligned with MediaContainerView visibility on the keyguard.
+     */
+    private fun isVisibilityValid(visibility: Int): Boolean {
+        val currentViewState = viewState as? MediaContainerViewState ?: return true
+        val shouldBeGone = !currentViewState.shouldBeVisible
+        return if (shouldBeGone) visibility == GONE else visibility != GONE
+    }
+
+    /**
+     * b/298213983
+     * MediaContainerView's visibility is changed to VISIBLE when it should be GONE.
+     * This method check this state and logs.
+     */
+    private fun assertMediaContainerVisibility(visibility: Int) {
+        val currentViewState = viewState
+
+        if (currentViewState is MediaContainerViewState) {
+            if (!currentViewState.shouldBeVisible && visibility == VISIBLE) {
+                Log.wtf("MediaContainerView", "MediaContainerView should be GONE " +
+                        "but its visibility changed to VISIBLE")
+            }
+        }
+    }
+
+    fun setKeyguardVisibility(isVisible: Boolean) {
+        val currentViewState = viewState
+        if (currentViewState is MediaContainerViewState) {
+            currentViewState.shouldBeVisible = isVisible
+        }
+
+        visibility = if (isVisible) VISIBLE else GONE
+    }
+
+    override fun createExpandableViewState(): ExpandableViewState = MediaContainerViewState()
+
+    class MediaContainerViewState : ExpandableViewState() {
+        var shouldBeVisible: Boolean = false
+
+        override fun copyFrom(viewState: ViewState) {
+            super.copyFrom(viewState)
+            if (viewState is MediaContainerViewState) {
+                shouldBeVisible = viewState.shouldBeVisible
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 634de7a..1ef9a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -853,7 +853,7 @@
                 }
             }
             if (row.isHeadsUpAnimatingAway()) {
-                if (NotificationsImprovedHunAnimation.isEnabled()) {
+                if (NotificationsImprovedHunAnimation.isEnabled() && !ambientState.isDozing()) {
                     if (shouldHunAppearFromBottom(ambientState, childState)) {
                         // move to the bottom of the screen
                         childState.setYTranslation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b772158..db15144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1375,7 +1375,10 @@
                 || !mKeyguardStateController.canDismissLockScreen()
                 || mKeyguardViewMediator.isAnySimPinSecure()
                 || (mQsController.getExpanded() && trackingTouch)
-                || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED) {
+                || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED
+                // This last one causes a race condition when the shade resets. Don't send a 0
+                // and let StatusBarStateController process a keyguard state change instead
+                || 1f - fraction == 0f) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4fd33ba..5610ed9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -55,6 +55,7 @@
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -138,7 +139,7 @@
             Context context,
             @DisplayId int displayId,
             Handler mainThreadHandler,
-            Executor uiBgExecutor,
+            @Background Executor uiBgExecutor,
             NotificationVisibilityProvider visibilityProvider,
             HeadsUpManager headsUpManager,
             ActivityStarter activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index f73d089..3e3ea85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -315,8 +315,8 @@
         // TTL for satellite polling is one hour
         const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 60
 
-        // Let the system boot up (5s) and stabilize before we check for system support
-        const val MIN_UPTIME: Long = 1000 * 5
+        // Let the system boot up and stabilize before we check for system support
+        const val MIN_UPTIME: Long = 1000 * 60
 
         private const val TAG = "DeviceBasedSatelliteRepo"
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
index a078dd5..2ad4d36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
@@ -23,6 +23,7 @@
 import android.content.Context
 import android.content.Intent
 import android.net.Uri
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.res.R
 import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
@@ -34,7 +35,7 @@
 class BatteryStateNotifier @Inject constructor(
     val controller: BatteryController,
     val noMan: NotificationManager,
-    val delayableExecutor: DelayableExecutor,
+    @Background val delayableExecutor: DelayableExecutor,
     val context: Context
 ) : BatteryController.BatteryStateChangeCallback {
     var stateUnknown = false
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 6124f63..2cad844 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -124,15 +124,6 @@
     }
 
     /**
-     * Provide a Background-Thread Executor by default.
-     */
-    @Provides
-    @SysUISingleton
-    public static Executor provideExecutor(@Background Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
      * Provide a BroadcastRunning Executor (for sending and receiving broadcasts).
      */
     @Provides
@@ -174,15 +165,6 @@
     }
 
     /**
-     * Provide a Background-Thread Executor by default.
-     */
-    @Provides
-    @SysUISingleton
-    public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
      * Provide a Background-Thread Executor.
      */
     @Provides
@@ -193,15 +175,6 @@
     }
 
     /**
-     * Provide a Background-Thread Executor by default.
-     */
-    @Provides
-    @SysUISingleton
-    public static RepeatableExecutor provideRepeatableExecutor(@Background DelayableExecutor exec) {
-        return new RepeatableExecutorImpl(exec);
-    }
-
-    /**
      * Provide a Background-Thread Executor.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
index 9b72eb7..5979f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -28,6 +28,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
@@ -94,10 +95,12 @@
         }
     };
 
+    // TODO: b/326449074 - Ensure the DelayableExecutor is on the correct thread, and update the
+    //                     qualifier (to @Main) or name (to bgExecutor) to be consistent with that.
     @Inject
     public PersistentConnectionManager(
             SystemClock clock,
-            DelayableExecutor mainExecutor,
+            @Background DelayableExecutor mainExecutor,
             DumpManager dumpManager,
             @Named(DUMPSYS_NAME) String dumpsysName,
             @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 139d190..65dede8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,6 +25,7 @@
 import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
+import static com.android.server.notification.Flags.screenshareNotificationHiding;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -69,6 +70,7 @@
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleEntry;
@@ -102,6 +104,7 @@
     private final NotificationVisibilityProvider mVisibilityProvider;
     private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
     private final NotificationLockscreenUserManager mNotifUserManager;
+    private final SensitiveNotificationProtectionController mSensitiveNotifProtectionController;
     private final CommonNotifCollection mCommonNotifCollection;
     private final NotifPipeline mNotifPipeline;
     private final NotifPipelineFlags mNotifPipelineFlags;
@@ -111,6 +114,7 @@
     // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
     private final List<NotifCallback> mCallbacks = new ArrayList<>();
     private final StatusBarWindowCallback mStatusBarWindowCallback;
+    private final Runnable mSensitiveStateChangedListener;
     private boolean mPanelExpanded;
 
     /**
@@ -130,6 +134,7 @@
             VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
+            SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
             CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
@@ -149,6 +154,7 @@
                     visualInterruptionDecisionProvider,
                     zenModeController,
                     notifUserManager,
+                    sensitiveNotificationProtectionController,
                     notifCollection,
                     notifPipeline,
                     sysUiState,
@@ -173,6 +179,7 @@
             VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
+            SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
             CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
@@ -188,6 +195,7 @@
         mVisibilityProvider = visibilityProvider;
         mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider;
         mNotifUserManager = notifUserManager;
+        mSensitiveNotifProtectionController = sensitiveNotificationProtectionController;
         mCommonNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
         mNotifPipelineFlags = notifPipelineFlags;
@@ -251,6 +259,22 @@
                 };
         notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
 
+        mSensitiveStateChangedListener = new Runnable() {
+            @Override
+            public void run() {
+                if (!screenshareNotificationHiding()) {
+                    return;
+                }
+                bubbles.onSensitiveNotificationProtectionStateChanged(
+                        mSensitiveNotifProtectionController.isSensitiveStateActive());
+            }
+        };
+
+        if (screenshareNotificationHiding()) {
+            mSensitiveNotifProtectionController
+                    .registerSensitiveStateListener(mSensitiveStateChangedListener);
+        }
+
         mSysuiProxy = new Bubbles.SysuiProxy() {
             @Override
             public void isNotificationPanelExpand(Consumer<Boolean> callback) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 1205dce..711f90f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
@@ -40,7 +39,6 @@
 class DefaultIndicationAreaSectionTest : SysuiTestCase() {
 
     @Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
-    @Mock private lateinit var aodAlphaViewModel: AodAlphaViewModel
     @Mock private lateinit var indicationController: KeyguardIndicationController
 
     private lateinit var underTest: DefaultIndicationAreaSection
@@ -52,7 +50,6 @@
             DefaultIndicationAreaSection(
                 context,
                 keyguardIndicationAreaViewModel,
-                aodAlphaViewModel,
                 indicationController,
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 14fe182..7f3d79f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -27,12 +27,16 @@
 import android.media.session.MediaController
 import android.media.session.MediaController.PlaybackInfo
 import android.media.session.MediaSession
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.settingslib.flags.Flags
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.media.PhoneMediaDevice
@@ -83,6 +87,7 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 public class MediaDeviceManagerTest : SysuiTestCase() {
+    @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
 
     private lateinit var manager: MediaDeviceManager
     @Mock private lateinit var controllerFactory: MediaControllerFactory
@@ -668,7 +673,28 @@
     }
 
     @Test
-    fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+    fun onBroadcastStarted_flagOff_currentMediaDeviceDataIsBroadcasting() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(BROADCAST_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val data = captureDeviceData(KEY)
+        assertThat(data.showBroadcastButton).isFalse()
+        assertThat(data.enabled).isTrue()
+        assertThat(data.name).isEqualTo(DEVICE_NAME)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStarted_legacy_currentMediaDeviceDataIsBroadcasting() {
         val broadcastCallback = setupBroadcastCallback()
         setupLeAudioConfiguration(true)
         setupBroadcastPackage(BROADCAST_APP_NAME)
@@ -686,7 +712,9 @@
     }
 
     @Test
-    fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting() {
         val broadcastCallback = setupBroadcastCallback()
         setupLeAudioConfiguration(true)
         setupBroadcastPackage(NORMAL_APP_NAME)
@@ -703,6 +731,62 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStopped_legacy_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(false)
+        broadcastCallback.onBroadcastStopped(1, 1)
+
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val data = captureDeviceData(KEY)
+        assertThat(data.showBroadcastButton).isFalse()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(BROADCAST_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val data = captureDeviceData(KEY)
+        assertThat(data.showBroadcastButton).isFalse()
+        assertThat(data.enabled).isFalse()
+        assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(NORMAL_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val data = captureDeviceData(KEY)
+        assertThat(data.showBroadcastButton).isFalse()
+        assertThat(data.enabled).isFalse()
+        assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     fun onBroadcastStopped_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
         val broadcastCallback = setupBroadcastCallback()
         setupLeAudioConfiguration(false)
@@ -714,6 +798,8 @@
 
         val data = captureDeviceData(KEY)
         assertThat(data.showBroadcastButton).isFalse()
+        assertThat(data.name?.equals(context.getString(R.string.audio_sharing_description)))
+            .isFalse()
     }
 
     private fun captureCallback(): LocalMediaManager.DeviceCallback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
new file mode 100644
index 0000000..0c32470
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.Intent
+import android.os.Process.myUserHandle
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+class ActionIntentExecutorTest : SysuiTestCase() {
+
+    private val scheduler = TestCoroutineScheduler()
+    private val mainDispatcher = StandardTestDispatcher(scheduler)
+    private val testScope = TestScope(mainDispatcher)
+    private val testableContext = TestableContext(mContext)
+
+    private val activityManagerWrapper = mock<ActivityManagerWrapper>()
+    private val displayTracker = mock<DisplayTracker>()
+    private val keyguardController = mock<ScreenshotKeyguardController>()
+
+    private val actionIntentExecutor =
+        ActionIntentExecutor(
+            testableContext,
+            activityManagerWrapper,
+            testScope,
+            mainDispatcher,
+            displayTracker,
+            keyguardController,
+        )
+
+    @Test
+    @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
+    fun launchIntent_callsCloseSystemWindows() =
+        testScope.runTest {
+            val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
+            val userHandle = myUserHandle()
+
+            actionIntentExecutor.launchIntent(intent, null, userHandle, false)
+            scheduler.advanceUntilIdle()
+
+            verify(activityManagerWrapper)
+                .closeSystemWindows(CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 032ec74..774aa51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -371,7 +371,6 @@
 
         val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
         verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-        captor.value.onBeforeUserSwitching(newID)
         captor.value.onUserSwitching(newID, userSwitchingReply)
 
         assertThat(callback.calledOnUserChanging).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
index 0b4de34..402d9aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
@@ -18,12 +18,13 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QS
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -43,13 +44,15 @@
     @get:Rule val expect: Expect = Expect.create()
 
     @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var qS: QS
+    private var qS: QS? = null
 
     private lateinit var controller: LockscreenShadeQsTransitionController
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        qS = mock()
+
         setTransitionDistance(TRANSITION_DISTANCE)
         setTransitionDelay(TRANSITION_DELAY)
         setSquishTransitionDistance(SQUISH_TRANSITION_DISTANCE)
@@ -220,7 +223,7 @@
 
         controller.dragDownAmount = rawDragAmount
 
-        verify(qS)
+        verify(qS!!)
             .setTransitionToFullShadeProgress(
                 /* isTransitioningToFullShade= */ true,
                 /* transitionFraction= */ controller.qsTransitionFraction,
@@ -228,6 +231,15 @@
             )
     }
 
+    @Test
+    fun nullQS_onDragAmountChanged_doesNotCrash() {
+        qS = null
+
+        val rawDragAmount = 200f
+
+        controller.dragDownAmount = rawDragAmount
+    }
+
     private fun setTransitionDistance(value: Int) {
         overrideResource(R.dimen.lockscreen_shade_qs_transition_distance, value)
         configurationController.notifyConfigurationChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 91701b1..86116a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -18,6 +18,7 @@
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeLockscreenInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -82,6 +83,8 @@
     private val testScope
         get() = testComponent.testScope
 
+    private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
+
     lateinit var row: ExpandableNotificationRow
 
     @Mock lateinit var centralSurfaces: CentralSurfaces
@@ -189,6 +192,7 @@
                 splitShadeStateController = ResourcesSplitShadeStateController(),
                 shadeLockscreenInteractorLazy = {shadeLockscreenInteractor},
                 naturalScrollingSettingObserver = naturalScrollingSettingObserver,
+                lazyQSSceneAdapter = { qsSceneAdapter }
             )
 
         transitionController.addCallback(transitionControllerCallback)
@@ -567,6 +571,16 @@
         verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
     }
 
+    @Test
+    fun nullQs_canDragDownFromAdapter() {
+        transitionController.qS = null
+
+        qsSceneAdapter.isQsFullyCollapsed = true
+        assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+        qsSceneAdapter.isQsFullyCollapsed = false
+        assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
+    }
+
     private fun enableSplitShade() {
         setSplitShadeEnabled(true)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
index 81d5c4d..700fb1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -9,6 +9,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -31,7 +32,7 @@
 
     @Mock private lateinit var scrimController: ScrimController
     @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
-    @Mock private lateinit var qS: QS
+    private var qS: QS? = null
     @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
     @Mock private lateinit var dumpManager: DumpManager
 
@@ -40,6 +41,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        qS = mock()
 
         whenever(nsslController.height).thenReturn(1800)
 
@@ -92,7 +94,7 @@
         setDragAmount(1000f)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
         setDragAmount(999f)
-        reset(qS, scrimController, nsslController)
+        reset(qS!!, scrimController, nsslController)
 
         setDragAmount(998f)
         setDragAmount(997f)
@@ -100,8 +102,15 @@
         verifyNoMoreOverScrollChanges()
     }
 
+    @Test
+    fun qsNull_applyOverscroll_doesNotCrash() {
+        qS = null
+
+        setDragAmount(100f)
+    }
+
     private fun verifyOverScrollPerformed() {
-        verify(qS).setOverScrollAmount(intThat { it > 0 })
+        verify(qS!!).setOverScrollAmount(intThat { it > 0 })
         verify(scrimController).setNotificationsOverScrollAmount(intThat { it > 0 })
         verify(nsslController).setOverScrollAmount(intThat { it > 0 })
     }
@@ -109,7 +118,7 @@
     private fun verifyOverScrollResetToZero() {
         // Might be more than once as the animator might have multiple values close to zero that
         // round down to zero.
-        verify(qS, atLeast(1)).setOverScrollAmount(0)
+        verify(qS!!, atLeast(1)).setOverScrollAmount(0)
         verify(scrimController, atLeast(1)).setNotificationsOverScrollAmount(0)
         verify(nsslController, atLeast(1)).setOverScrollAmount(0)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 8b99811..a12806b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -167,6 +167,20 @@
     }
 
     @Test
+    fun testUpdateIcons_sensitiveImportantConversation() {
+        val entry =
+            notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+        entry?.setSensitive(true, true)
+        entry?.channel?.isImportantConversation = true
+        entry?.let { iconManager.createIcons(it) }
+        // Updating the icons after creation shouldn't break anything
+        entry?.let { iconManager.updateIcons(it) }
+        assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+        assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+        assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+    }
+
+    @Test
     fun testUpdateIcons_sensitivityChange() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 91a9da3..995da81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -222,6 +222,26 @@
     }
 
     @Test
+    @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+    fun resetViewStates_hunAnimatingAwayWhileDozing_yTranslationIsInset() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+        ambientState.isDozing = true
+
+        resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
+    }
+
+    @Test
+    @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+    fun resetViewStates_hunAnimatingAwayWhileDozing_hasStackMargin_changesHunYTranslation() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+        ambientState.isDozing = true
+
+        resetViewStates_stackMargin_changesHunYTranslation()
+    }
+
+    @Test
     fun resetViewStates_hunsOverlapping_bottomHunClipped() {
         val topHun = mockExpandableNotificationRow()
         val bottomHun = mockExpandableNotificationRow()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
index 203096a..08b49f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
@@ -166,4 +166,28 @@
             assertThat(config.color).isEqualTo(expectedColor)
         }
     }
+
+    @Test
+    fun play_initializesShader() {
+        val expectedNoiseOffset = floatArrayOf(0.1f, 0.2f, 0.3f)
+        val config =
+            TurbulenceNoiseAnimationConfig(
+                noiseOffsetX = expectedNoiseOffset[0],
+                noiseOffsetY = expectedNoiseOffset[1],
+                noiseOffsetZ = expectedNoiseOffset[2]
+            )
+        val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+        val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+        fakeExecutor.execute {
+            turbulenceNoiseController.play(SIMPLEX_NOISE, config)
+
+            assertThat(turbulenceNoiseView.noiseConfig).isNotNull()
+            val shader = turbulenceNoiseView.turbulenceNoiseShader!!
+            assertThat(shader).isNotNull()
+            assertThat(shader.noiseOffsetX).isEqualTo(expectedNoiseOffset[0])
+            assertThat(shader.noiseOffsetY).isEqualTo(expectedNoiseOffset[1])
+            assertThat(shader.noiseOffsetZ).isEqualTo(expectedNoiseOffset[2])
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index b25ac24..a930860 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -26,6 +26,7 @@
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -46,6 +47,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
@@ -73,6 +75,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
@@ -161,6 +165,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
@@ -257,6 +262,8 @@
     private NotificationShadeWindowView mNotificationShadeWindowView;
     @Mock
     private AuthController mAuthController;
+    @Mock
+    private SensitiveNotificationProtectionController mSensitiveNotificationProtectionController;
 
     private SysUiState mSysUiState;
     private boolean mSysUiStateBubblesExpanded;
@@ -272,6 +279,8 @@
     private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
     @Captor
     private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor;
+    @Captor
+    private ArgumentCaptor<Runnable> mSensitiveStateChangedListener;
 
     private BubblesManager mBubblesManager;
     private TestableBubbleController mBubbleController;
@@ -594,6 +603,7 @@
                 interruptionDecisionProvider,
                 mZenModeController,
                 mLockscreenUserManager,
+                mSensitiveNotificationProtectionController,
                 mCommonNotifCollection,
                 mNotifPipeline,
                 mSysUiState,
@@ -2203,6 +2213,33 @@
         assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
     }
 
+    @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+    @Test
+    public void doesNotRegisterSensitiveStateListener() {
+        verifyZeroInteractions(mSensitiveNotificationProtectionController);
+    }
+
+    @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+    @Test
+    public void registerSensitiveStateListener() {
+        verify(mSensitiveNotificationProtectionController).registerSensitiveStateListener(any());
+    }
+
+    @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+    @Test
+    public void onSensitiveNotificationProtectionStateChanged() {
+        verify(mSensitiveNotificationProtectionController, atLeastOnce())
+                .registerSensitiveStateListener(mSensitiveStateChangedListener.capture());
+
+        when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(true);
+        mSensitiveStateChangedListener.getValue().run();
+        verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(true);
+
+        when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(false);
+        mSensitiveStateChangedListener.getValue().run();
+        verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(false);
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 6af08d3..f74cf71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.settings.userTracker
 import com.android.systemui.smartspace.data.repository.smartspaceRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
@@ -46,6 +47,7 @@
         appWidgetHost = mock(),
         keyguardInteractor = keyguardInteractor,
         editWidgetsActivityStarter = editWidgetsActivityStarter,
+        userTracker = userTracker,
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
         communalSettingsInteractor = communalSettingsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index b1581d1..4d902fa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -41,6 +41,8 @@
     private val _navBarPadding = MutableStateFlow<Int>(0)
     val navBarPadding = _navBarPadding.asStateFlow()
 
+    override var isQsFullyCollapsed: Boolean = true
+
     override suspend fun inflate(context: Context) {
         _view.value = inflateDelegate(context)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt
new file mode 100644
index 0000000..00ab0b5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.adapter
+
+import android.view.View
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.fakeQSSceneAdapter by Kosmos.Fixture { FakeQSSceneAdapter({ mock<View>() }) }
+
+val Kosmos.qsSceneAdapter: QSSceneAdapter by Kosmos.Fixture { fakeQSSceneAdapter }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index e5072f1..e4a3896 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
 import com.android.systemui.plugins.activityStarter
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
@@ -61,5 +62,6 @@
         splitShadeStateController = splitShadeStateController,
         shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
         naturalScrollingSettingObserver = naturalScrollingSettingObserver,
+        lazyQSSceneAdapter = { qsSceneAdapter }
     )
 }
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index 71f2b9e..e9f959f 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -35,6 +35,15 @@
 }
 
 flag {
+    name: "enable_v_to_u_restore_for_system_components_in_allowlist"
+    namespace: "onboarding"
+    description: "Enables system components to opt in to support restore in V to U downgrade "
+            "scenario without opting in for restoreAnyVersion."
+    bug: "324233962"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "enable_increase_datatypes_for_agent_logging"
     namespace: "onboarding"
     description: "Increase the number of a supported datatypes that an agent can define for its "
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 9f0deea..6e98e68 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -177,6 +177,10 @@
         return mHasMetadata;
     }
 
+    public int getSourceSdk() {
+        return mStoredSdkVersion;
+    }
+
     public Metadata getRestoredMetadata(String packageName) {
         if (mRestoredSignatures == null) {
             Slog.w(TAG, "getRestoredMetadata() before metadata read!");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index d85dd87..e666442 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -44,6 +44,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -51,6 +52,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Slog;
 
@@ -82,6 +84,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -158,6 +161,12 @@
     // When finished call listener
     private final OnTaskFinishedListener mListener;
 
+    // List of packages that support  V-> U downgrade but do not have RestoreAnyVersion set to true.
+    private List<String> mVToUAllowlist;
+
+    // List of packages that have RestoreAnyVersion set to true but do not support  V-> U downgrade.
+    private List<String> mVToUDenylist;
+
     // Key/value: bookkeeping about staged data and files for agent access
     private File mBackupDataName;
     private File mStageName;
@@ -172,7 +181,8 @@
     @VisibleForTesting
     PerformUnifiedRestoreTask(
             UserBackupManagerService backupManagerService,
-            TransportConnection transportConnection) {
+            TransportConnection transportConnection,
+            String vToUAllowlist, String vToUDenyList) {
         mListener = null;
         mAgentTimeoutParameters = null;
         mOperationStorage = null;
@@ -183,6 +193,8 @@
         mBackupEligibilityRules = null;
         this.backupManagerService = backupManagerService;
         mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor= */ null);
+        mVToUAllowlist = createVToUList(vToUAllowlist);
+        mVToUDenylist = createVToUList(vToUDenyList);
     }
 
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
@@ -223,6 +235,18 @@
                         backupManagerService.getAgentTimeoutParameters(),
                         "Timeout parameters cannot be null");
         mBackupEligibilityRules = backupEligibilityRules;
+        mVToUAllowlist =
+            createVToUList(
+                Settings.Secure.getStringForUser(
+                    backupManagerService.getContext().getContentResolver(),
+                    Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+                    mUserId));
+        mVToUDenylist =
+            createVToUList(
+                Settings.Secure.getStringForUser(
+                    backupManagerService.getContext().getContentResolver(),
+                    Settings.Secure.V_TO_U_RESTORE_DENYLIST,
+                    mUserId));
 
         if (targetPackage != null) {
             // Single package restore
@@ -636,60 +660,29 @@
                 // Data is from a "newer" version of the app than we have currently
                 // installed.  If the app has not declared that it is prepared to
                 // handle this case, we do not attempt the restore.
-                if ((mCurrentPackage.applicationInfo.flags
-                                & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
-                        == 0) {
-                    String message =
-                            "Source version "
-                                    + metaInfo.versionCode
-                                    + " > installed version "
-                                    + mCurrentPackage.getLongVersionCode();
-                    Slog.w(TAG, "Package " + pkgName + ": " + message);
-                    Bundle monitoringExtras =
-                            mBackupManagerMonitorEventSender.putMonitoringExtra(
-                                    null,
-                                    BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
-                                    metaInfo.versionCode);
-                    monitoringExtras =
-                            mBackupManagerMonitorEventSender.putMonitoringExtra(
-                                    monitoringExtras,
-                                    BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
-                                    false);
-                    monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
-                    mBackupManagerMonitorEventSender.monitorEvent(
-                            BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
-                            mCurrentPackage,
-                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
-                            monitoringExtras);
-                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
-                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
-                    return;
-                } else {
-                    if (DEBUG) {
-                        Slog.v(
-                                TAG,
-                                "Source version "
-                                        + metaInfo.versionCode
-                                        + " > installed version "
-                                        + mCurrentPackage.getLongVersionCode()
-                                        + " but restoreAnyVersion");
+                if (mIsSystemRestore
+                    && isVToUDowngrade(mPmAgent.getSourceSdk(), android.os.Build.VERSION.SDK_INT)) {
+                    if (isPackageEligibleForVToURestore(mCurrentPackage)) {
+                        Slog.i(TAG, "Package " + pkgName
+                                + " is eligible for V to U downgrade scenario");
+                    } else {
+                        String message = "Package not eligible for V to U downgrade scenario";
+                        Slog.i(TAG, pkgName + " : " + message);
+                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
+                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
+                        return;
                     }
-                    Bundle monitoringExtras =
-                            mBackupManagerMonitorEventSender.putMonitoringExtra(
-                                    null,
-                                    BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
-                                    metaInfo.versionCode);
-                    monitoringExtras =
-                            mBackupManagerMonitorEventSender.putMonitoringExtra(
-                                    monitoringExtras,
-                                    BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
-                                    true);
-                    monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
-                    mBackupManagerMonitorEventSender.monitorEvent(
-                            BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
-                            mCurrentPackage,
-                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
-                            monitoringExtras);
+                } else {
+                    if ((mCurrentPackage.applicationInfo.flags
+                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+                            == 0) {
+                        // Downgrade scenario with RestoreAnyVersion flag off
+                        logDowngradeScenario(/* isRestoreAnyVersion */ false, metaInfo);
+                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
+                        return;
+                    } else {
+                        logDowngradeScenario(/* isRestoreAnyVersion */ true, metaInfo);
+                    }
                 }
             }
 
@@ -1673,4 +1666,86 @@
         return mBackupManagerMonitorEventSender.putMonitoringExtra(
                 extras, BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, RESTORE);
     }
+
+    // checks the sdk of the target/source device for a B&R operation.
+    // system components can opt in/out of V->U restore via allowlists. All other apps are
+    // not impacted
+    @SuppressWarnings("AndroidFrameworkCompatChange")
+    @VisibleForTesting
+    protected boolean isVToUDowngrade(int sourceSdk, int targetSdk) {
+        // We assume that if the source sdk is greater than U then the source is V.
+        return Flags.enableVToURestoreForSystemComponentsInAllowlist()
+                && (sourceSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+                && (targetSdk == Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+    }
+
+    @VisibleForTesting
+    protected List<String> createVToUList(@Nullable String listString) {
+        // The allowlist/denylist is stored as a comma-separated list of package names
+        List<String> list = new ArrayList<>();
+        if (listString != null) {
+            list = Arrays.asList(listString.split(","));
+        }
+        return list;
+    }
+
+    @VisibleForTesting
+    protected boolean isPackageEligibleForVToURestore(PackageInfo mCurrentPackage) {
+        // A package is eligible for V to U downgrade restore if either:
+        //    - The package has restoreAnyVersion set to false and is part of the V to U allowlist
+        //      (and not in the denylist)
+        //    - The package has restoreAnyVersion set to true and is not part of the denylist
+        if (mVToUDenylist.contains(mCurrentPackage.packageName)){
+            return false;
+        } else if ((mCurrentPackage.applicationInfo.flags
+                & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+                == 0) {
+            // package has restoreAnyVersion set to false
+            return mVToUAllowlist.contains(mCurrentPackage.packageName);
+        } else {
+            // package has restoreAnyVersion set to true and is nor in denylist
+            return true;
+        }
+    }
+
+    private void logDowngradeScenario(boolean isRestoreAnyVersion, Metadata metaInfo) {
+        Bundle monitoringExtras =
+                mBackupManagerMonitorEventSender.putMonitoringExtra(
+                        null,
+                        BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
+                        metaInfo.versionCode);
+        String message;
+        if (isRestoreAnyVersion) {
+            monitoringExtras =
+                    mBackupManagerMonitorEventSender.putMonitoringExtra(
+                            monitoringExtras,
+                            BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+                            true);
+            message = "Source version "
+                    + metaInfo.versionCode
+                    + " > installed version "
+                    + mCurrentPackage.getLongVersionCode()
+                    + " but restoreAnyVersion";
+        } else {
+            monitoringExtras =
+                    mBackupManagerMonitorEventSender.putMonitoringExtra(
+                            monitoringExtras,
+                            BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+                            false);
+            message = "Source version "
+                    + metaInfo.versionCode
+                    + " > installed version "
+                    + mCurrentPackage.getLongVersionCode();
+            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName,
+                    message);
+        }
+        Slog.i(TAG, "Package " + mCurrentPackage.packageName + ": " + message);
+        monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
+        mBackupManagerMonitorEventSender.monitorEvent(
+                BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
+                mCurrentPackage,
+                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                monitoringExtras);
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 767f54d..966fe5b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -120,6 +120,7 @@
     static final String[] sDeviceConfigAconfigScopes = new String[] {
         "accessibility",
         "android_core_networking",
+        "android_stylus",
         "aoc",
         "app_widgets",
         "arc_next",
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index bba5ba3..631e751 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -37,9 +37,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.utils.DebugUtils;
 import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -79,7 +81,7 @@
 
     // Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
     @NonNull
-    private HashMap<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
+    private Map<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
 
     // Current throttling data being used.
     // Null if we do not support throttling.
@@ -97,6 +99,10 @@
     // The brightness throttling configuration that should be used.
     private String mThermalBrightnessThrottlingDataId;
 
+    // Temperature Sensor to be monitored for throttling.
+    @NonNull
+    private SensorData mTempSensor;
+
     // This is a collection of brightness throttling data that has been written as overrides from
     // the DeviceConfig. This will always take priority over the display device config data.
     // We need to store the data for every display device, so we do not need to update this each
@@ -121,17 +127,19 @@
 
     BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
             String throttlingDataId,
-            @NonNull HashMap<String, ThermalBrightnessThrottlingData>
-                    thermalBrightnessThrottlingDataMap) {
-        this(new Injector(), handler, handler, throttlingChangeCallback,
-                uniqueDisplayId, throttlingDataId, thermalBrightnessThrottlingDataMap);
+            @NonNull DisplayDeviceConfig displayDeviceConfig) {
+        this(new Injector(), handler, handler, throttlingChangeCallback, uniqueDisplayId,
+                throttlingDataId,
+                displayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+                displayDeviceConfig.getTempSensor());
     }
 
     @VisibleForTesting
     BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
             Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
-            @NonNull HashMap<String, ThermalBrightnessThrottlingData>
-                    thermalBrightnessThrottlingDataMap) {
+            @NonNull Map<String, ThermalBrightnessThrottlingData>
+                    thermalBrightnessThrottlingDataMap,
+            @NonNull SensorData tempSensor) {
         mInjector = injector;
 
         mHandler = handler;
@@ -147,7 +155,7 @@
         mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
         loadThermalBrightnessThrottlingDataFromDeviceConfig();
         loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThermalThrottlingDataMap,
-                mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
+                tempSensor, mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
     }
 
     boolean deviceSupportsThrottling() {
@@ -180,12 +188,14 @@
     }
 
     void loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
-            HashMap<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+            Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+            SensorData tempSensor,
             String brightnessThrottlingDataId,
             String uniqueDisplayId) {
         mDdcThermalThrottlingDataMap = ddcThrottlingDataMap;
         mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId;
         mUniqueDisplayId = uniqueDisplayId;
+        mTempSensor = tempSensor;
         resetThermalThrottlingData();
     }
 
@@ -310,7 +320,7 @@
         }
 
         if (deviceSupportsThrottling()) {
-            mSkinThermalStatusObserver.startObserving();
+            mSkinThermalStatusObserver.startObserving(mTempSensor);
         }
     }
 
@@ -357,6 +367,7 @@
     private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
         private final Injector mInjector;
         private final Handler mHandler;
+        private SensorData mObserverTempSensor;
 
         private IThermalService mThermalService;
         private boolean mStarted;
@@ -371,28 +382,51 @@
             if (DEBUG) {
                 Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
             }
+
+            if (mObserverTempSensor.name != null
+                    && !mObserverTempSensor.name.equals(temp.getName())) {
+                Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+                            + mObserverTempSensor.name
+                            + " != notified sensor: "
+                            + temp.getName());
+                return;
+            }
             mHandler.post(() -> {
                 final @Temperature.ThrottlingStatus int status = temp.getStatus();
                 thermalStatusChanged(status);
             });
         }
 
-        void startObserving() {
-            if (mStarted) {
+        void startObserving(SensorData tempSensor) {
+            if (!mStarted || mObserverTempSensor == null) {
+                mObserverTempSensor = tempSensor;
+                registerThermalListener();
+                return;
+            }
+
+            String curType = mObserverTempSensor.type;
+            mObserverTempSensor = tempSensor;
+            if (curType.equals(tempSensor.type)) {
                 if (DEBUG) {
                     Slog.d(TAG, "Thermal status observer already started");
                 }
                 return;
             }
+            stopObserving();
+            registerThermalListener();
+        }
+
+        void registerThermalListener() {
             mThermalService = mInjector.getThermalService();
             if (mThermalService == null) {
                 Slog.e(TAG, "Could not observe thermal status. Service not available");
                 return;
             }
+            int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
             try {
                 // We get a callback immediately upon registering so there's no need to query
                 // for the current value.
-                mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+                mThermalService.registerThermalEventListenerWithType(this, temperatureType);
                 mStarted = true;
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to register thermal status listener", e);
@@ -418,6 +452,7 @@
         void dump(PrintWriter writer) {
             writer.println("  SkinThermalStatusObserver:");
             writer.println("    mStarted: " + mStarted);
+            writer.println("    mObserverTempSensor: " + mObserverTempSensor);
             if (mThermalService != null) {
                 writer.println("    ThermalService available");
             } else {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d1374a5..9b2dcc5 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -384,6 +384,10 @@
  *             </point>
  *          </supportedModes>
  *      </proxSensor>
+ *      <tempSensor>
+ *        <type>DISPLAY</type>
+ *        <name>VIRTUAL-SKIN-DISPLAY</name>
+ *      </tempSensor>
  *
  *      <ambientLightHorizonLong>10001</ambientLightHorizonLong>
  *      <ambientLightHorizonShort>2001</ambientLightHorizonShort>
@@ -625,6 +629,12 @@
     @Nullable
     private SensorData mProximitySensor;
 
+    // The details of the temperature sensor associated with this display.
+    // Throttling will be based on thermal status of this sensor.
+    // For empty values default back to sensor of TYPE_SKIN.
+    @NonNull
+    private SensorData mTempSensor;
+
     private final List<RefreshRateLimitation> mRefreshRateLimitations =
             new ArrayList<>(2 /*initialCapacity*/);
 
@@ -821,10 +831,10 @@
     private String mLowBlockingZoneThermalMapId = null;
     private String mHighBlockingZoneThermalMapId = null;
 
-    private final HashMap<String, ThermalBrightnessThrottlingData>
+    private final Map<String, ThermalBrightnessThrottlingData>
             mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
 
-    private final HashMap<String, PowerThrottlingData>
+    private final Map<String, PowerThrottlingData>
             mPowerThrottlingDataMapByThrottlingId = new HashMap<>();
 
     private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
@@ -1489,6 +1499,13 @@
         return mProximitySensor;
     }
 
+    /**
+     * @return temperature sensor data associated with the display.
+     */
+    public SensorData getTempSensor() {
+        return mTempSensor;
+    }
+
     boolean isAutoBrightnessAvailable() {
         return mAutoBrightnessAvailable;
     }
@@ -1539,7 +1556,7 @@
     /**
      * @return brightness throttling configuration data for this display, for each throttling id.
      */
-    public HashMap<String, ThermalBrightnessThrottlingData>
+    public Map<String, ThermalBrightnessThrottlingData>
             getThermalBrightnessThrottlingDataMapByThrottlingId() {
         return mThermalBrightnessThrottlingDataMapByThrottlingId;
     }
@@ -1558,7 +1575,7 @@
     /**
      * @return power throttling configuration data for this display, for each throttling id.
      **/
-    public HashMap<String, PowerThrottlingData>
+    public Map<String, PowerThrottlingData>
             getPowerThrottlingDataMapByThrottlingId() {
         return mPowerThrottlingDataMapByThrottlingId;
     }
@@ -1871,6 +1888,7 @@
                 + "mAmbientLightSensor=" + mAmbientLightSensor
                 + ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor
                 + ", mProximitySensor=" + mProximitySensor
+                + ", mTempSensor=" + mTempSensor
                 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
                 + ", mDensityMapping= " + mDensityMapping
                 + ", mAutoBrightnessBrighteningLightDebounce= "
@@ -1972,6 +1990,7 @@
                         mContext.getResources());
                 mScreenOffBrightnessSensor = SensorData.loadScreenOffBrightnessSensorConfig(config);
                 mProximitySensor = SensorData.loadProxSensorConfig(config);
+                mTempSensor = SensorData.loadTempSensorConfig(mFlags, config);
                 loadAmbientHorizonFromDdc(config);
                 loadBrightnessChangeThresholds(config);
                 loadAutoBrightnessConfigValues(config);
@@ -1999,6 +2018,7 @@
         loadBrightnessRampsFromConfigXml();
         mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
         mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+        mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
         loadBrightnessChangeThresholdsFromXml();
         loadAutoBrightnessConfigsFromConfigXml();
         loadAutoBrightnessAvailableFromConfigXml();
@@ -2026,6 +2046,7 @@
         setSimpleMappingStrategyValues();
         mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
         mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+        mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
         loadAutoBrightnessAvailableFromConfigXml();
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d5863a7..3965d55 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -861,6 +861,7 @@
                 mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
                 mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
                         config.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+                        config.getTempSensor(),
                         mThermalBrightnessThrottlingDataId,
                         mUniqueDisplayId);
             }
@@ -923,6 +924,7 @@
         mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
         mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
                 mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+                mDisplayDeviceConfig.getTempSensor(),
                 mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
     }
 
@@ -1996,7 +1998,7 @@
                     postBrightnessChangeRunnable();
                 }, mUniqueDisplayId,
                 mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
-                ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
+                ddConfig);
     }
 
     private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index bc5fcb4..18e8fab 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -38,6 +38,7 @@
 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
 import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.config.SensorData;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.feature.DisplayManagerFlags;
 
@@ -336,5 +337,10 @@
         public float getBrightnessWearBedtimeModeCap() {
             return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
         }
+
+        @NonNull
+        public SensorData getTempSensor() {
+            return mDisplayDeviceConfig.getTempSensor();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 944a8a6..4498258 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -35,8 +35,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -49,9 +51,8 @@
         BrightnessClamper<BrightnessThermalClamper.ThermalData> {
 
     private static final String TAG = "BrightnessThermalClamper";
-
-    @Nullable
-    private final IThermalService mThermalService;
+    @NonNull
+    private final ThermalStatusObserver mThermalStatusObserver;
     @NonNull
     private final DeviceConfigParameterProvider mConfigParameterProvider;
     // data from DeviceConfig, for all displays, for all dataSets
@@ -66,7 +67,6 @@
     // otherwise mDataFromDeviceConfig
     @Nullable
     private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
-    private boolean mStarted = false;
     @Nullable
     private String mUniqueDisplayId = null;
     @Nullable
@@ -74,14 +74,6 @@
     @Temperature.ThrottlingStatus
     private int mThrottlingStatus = Temperature.THROTTLING_NONE;
 
-    private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() {
-        @Override
-        public void notifyThrottling(Temperature temperature) {
-            @Temperature.ThrottlingStatus int status = temperature.getStatus();
-            mHandler.post(() -> thermalStatusChanged(status));
-        }
-    };
-
     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
         try {
             int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -105,12 +97,11 @@
     BrightnessThermalClamper(Injector injector, Handler handler,
             ClamperChangeListener listener, ThermalData thermalData) {
         super(handler, listener);
-        mThermalService = injector.getThermalService();
         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+        mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
         mHandler.post(() -> {
             setDisplayData(thermalData);
             loadOverrideData();
-            start();
         });
 
     }
@@ -139,32 +130,19 @@
 
     @Override
     void stop() {
-        if (!mStarted) {
-            return;
-        }
-        try {
-            mThermalService.unregisterThermalEventListener(mThermalEventListener);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to unregister thermal status listener", e);
-        }
-        mStarted = false;
+        mThermalStatusObserver.stopObserving();
     }
 
     @Override
     void dump(PrintWriter writer) {
         writer.println("BrightnessThermalClamper:");
-        writer.println("  mStarted: " + mStarted);
-        if (mThermalService != null) {
-            writer.println("  ThermalService available");
-        } else {
-            writer.println("  ThermalService not available");
-        }
         writer.println("  mThrottlingStatus: " + mThrottlingStatus);
         writer.println("  mUniqueDisplayId: " + mUniqueDisplayId);
         writer.println("  mDataId: " + mDataId);
         writer.println("  mDataOverride: " + mThermalThrottlingDataOverride);
         writer.println("  mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
         writer.println("  mDataActive: " + mThermalThrottlingDataActive);
+        mThermalStatusObserver.dump(writer);
         super.dump(writer);
     }
 
@@ -193,6 +171,7 @@
             Slog.wtf(TAG,
                     "Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
         }
+        mThermalStatusObserver.registerSensor(data.getTempSensor());
     }
 
     private void recalculateBrightnessCap() {
@@ -226,19 +205,91 @@
         }
     }
 
-    private void start() {
-        if (mThermalService == null) {
-            Slog.e(TAG, "Could not observe thermal status. Service not available");
-            return;
+
+    private final class ThermalStatusObserver extends IThermalEventListener.Stub {
+        private final Injector mInjector;
+        private final Handler mHandler;
+        private IThermalService mThermalService;
+        private boolean mStarted;
+        private SensorData mObserverTempSensor;
+
+        ThermalStatusObserver(Injector injector, Handler handler) {
+            mInjector = injector;
+            mHandler = handler;
+            mStarted = false;
         }
-        try {
-            // We get a callback immediately upon registering so there's no need to query
-            // for the current value.
-            mThermalService.registerThermalEventListenerWithType(mThermalEventListener,
-                    Temperature.TYPE_SKIN);
-            mStarted = true;
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to register thermal status listener", e);
+
+        void registerSensor(SensorData tempSensor) {
+            if (!mStarted || mObserverTempSensor == null) {
+                mObserverTempSensor = tempSensor;
+                registerThermalListener();
+                return;
+            }
+
+            String curType = mObserverTempSensor.type;
+            mObserverTempSensor = tempSensor;
+            if (curType.equals(tempSensor.type)) {
+                Slog.d(TAG, "Thermal status observer already started");
+                return;
+            }
+            stopObserving();
+            registerThermalListener();
+        }
+
+        void registerThermalListener() {
+            mThermalService = mInjector.getThermalService();
+            if (mThermalService == null) {
+                Slog.e(TAG, "Could not observe thermal status. Service not available");
+                return;
+            }
+            int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
+            try {
+                // We get a callback immediately upon registering so there's no need to query
+                // for the current value.
+                mThermalService.registerThermalEventListenerWithType(this, temperatureType);
+                mStarted = true;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register thermal status listener", e);
+            }
+        }
+
+        @Override
+        public void notifyThrottling(Temperature temp) {
+            Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
+            if (mObserverTempSensor.name != null
+                    && !mObserverTempSensor.name.equals(temp.getName())) {
+                Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+                            + mObserverTempSensor.name
+                            + " != notified sensor: "
+                            + temp.getName());
+                return;
+            }
+            @Temperature.ThrottlingStatus int status = temp.getStatus();
+            mHandler.post(() -> thermalStatusChanged(status));
+        }
+
+        void stopObserving() {
+            if (!mStarted) {
+                return;
+            }
+            try {
+                mThermalService.unregisterThermalEventListener(this);
+                mStarted = false;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to unregister thermal status listener", e);
+            }
+            mThermalService = null;
+        }
+
+        void dump(PrintWriter writer) {
+            writer.println("  ThermalStatusObserver:");
+            writer.println("    mStarted: " + mStarted);
+            writer.println("    mObserverTempSensor: " + mObserverTempSensor);
+            if (mThermalService != null) {
+                writer.println("    ThermalService available");
+            } else {
+                writer.println("    ThermalService not available");
+            }
         }
     }
 
@@ -251,6 +302,9 @@
 
         @Nullable
         ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
+
+        @NonNull
+        SensorData getTempSensor();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 3bb35bf..8e716f8 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -22,6 +22,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -32,6 +33,9 @@
  */
 public class SensorData {
 
+    public static final String TEMPERATURE_TYPE_DISPLAY = "DISPLAY";
+    public static final String TEMPERATURE_TYPE_SKIN = "SKIN";
+
     @Nullable
     public final String type;
     @Nullable
@@ -143,6 +147,32 @@
     }
 
     /**
+     * Loads temperature sensor data for no config case. (Type: SKIN, Name: null)
+     */
+    public static SensorData loadTempSensorUnspecifiedConfig() {
+        return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+    }
+
+    /**
+     * Loads temperature sensor data from given display config.
+     * If empty or null config given default to (Type: SKIN, Name: null)
+     */
+    public static SensorData loadTempSensorConfig(DisplayManagerFlags flags,
+            DisplayConfiguration config) {
+        SensorDetails sensorDetails = config.getTempSensor();
+        if (!flags.isSensorBasedBrightnessThrottlingEnabled() || sensorDetails == null) {
+            return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+        }
+        String name = sensorDetails.getName();
+        String type = sensorDetails.getType();
+        if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+            type = TEMPERATURE_TYPE_SKIN;
+            name = null;
+        }
+        return new SensorData(type, name);
+    }
+
+    /**
      * Loads sensor unspecified config, this means system should use default sensor.
      * See also {@link com.android.server.display.utils.SensorUtils}
      */
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 1ae2559..516d4b1 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -121,6 +121,11 @@
             Flags::refreshRateVotingTelemetry
     );
 
+    private final FlagState mSensorBasedBrightnessThrottling = new FlagState(
+            Flags.FLAG_SENSOR_BASED_BRIGHTNESS_THROTTLING,
+            Flags::sensorBasedBrightnessThrottling
+    );
+
     /**
      * @return {@code true} if 'port' is allowed in display layout configuration file.
      */
@@ -247,6 +252,10 @@
         return mRefreshRateVotingTelemetry.isEnabled();
     }
 
+    public boolean isSensorBasedBrightnessThrottlingEnabled() {
+        return mSensorBasedBrightnessThrottling.isEnabled();
+    }
+
     /**
      * dumps all flagstates
      * @param pw printWriter
@@ -270,6 +279,7 @@
         pw.println(" " + mAutoBrightnessModesFlagState);
         pw.println(" " + mFastHdrTransitions);
         pw.println(" " + mRefreshRateVotingTelemetry);
+        pw.println(" " + mSensorBasedBrightnessThrottling);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index c2f52b5..63ab3a9 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -184,3 +184,11 @@
     bug: "310029108"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "sensor_based_brightness_throttling"
+    namespace: "display_manager"
+    description: "Feature flag for enabling brightness throttling using sensor from config."
+    bug: "294900859"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index 8b9fe108..c63473a 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -16,9 +16,11 @@
 
 package com.android.server.display.utils;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.os.Temperature;
 import android.text.TextUtils;
 
 import com.android.server.display.config.SensorData;
@@ -70,4 +72,17 @@
         return null;
     }
 
+    /**
+     * Convert string temperature type to its corresponding integer value.
+     */
+    public static int getSensorTemperatureType(@NonNull SensorData tempSensor) {
+        if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_DISPLAY)) {
+            return Temperature.TYPE_DISPLAY;
+        } else if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_SKIN)) {
+            return Temperature.TYPE_SKIN;
+        }
+        throw new IllegalArgumentException(
+            "tempSensor doesn't support type: " + tempSensor.type);
+    }
+
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc7b873..3a7ac0b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8206,7 +8206,7 @@
                 try {
                     return mTelecomManager.isInManagedCall()
                             || mTelecomManager.isInSelfManagedCall(pkg,
-                            UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true);
+                            /* hasCrossUserAccess */ true);
                 } catch (IllegalStateException ise) {
                     // Telecom is not ready (this is likely early boot), so there are no calls.
                     return false;
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 7091c47..ecfc040 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.search;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
@@ -24,6 +25,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
 import android.os.Binder;
@@ -32,6 +34,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -47,6 +50,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -71,11 +75,6 @@
         }
 
         @Override
-        public void onUserUnlocking(@NonNull TargetUser user) {
-            mService.mHandler.post(() -> mService.onUnlockUser(user.getUserIdentifier()));
-        }
-
-        @Override
         public void onUserStopped(@NonNull TargetUser user) {
             mService.onCleanupUser(user.getUserIdentifier());
         }
@@ -102,10 +101,6 @@
     }
 
     private Searchables getSearchables(int userId) {
-        return getSearchables(userId, false);
-    }
-
-    private Searchables getSearchables(int userId, boolean forceUpdate) {
         final long token = Binder.clearCallingIdentity();
         try {
             final UserManager um = mContext.getSystemService(UserManager.class);
@@ -122,21 +117,11 @@
             Searchables searchables = mSearchables.get(userId);
             if (searchables == null) {
                 searchables = new Searchables(mContext, userId);
-                searchables.updateSearchableList();
-                mSearchables.append(userId, searchables);
-            } else if (forceUpdate) {
-                searchables.updateSearchableList();
+                mSearchables.put(userId, searchables);
             }
-            return searchables;
-        }
-    }
 
-    private void onUnlockUser(int userId) {
-        try {
-            getSearchables(userId, true);
-        } catch (IllegalStateException ignored) {
-            // We're just trying to warm a cache, so we don't mind if the user
-            // was stopped or destroyed before we got here.
+            searchables.updateSearchableListIfNeeded();
+            return searchables;
         }
     }
 
@@ -150,28 +135,110 @@
      * Refreshes the "searchables" list when packages are added/removed.
      */
     class MyPackageMonitor extends PackageMonitor {
+        /**
+         * Packages that are appeared, disappeared, or modified for whatever reason.
+         */
+        private final ArrayList<String> mChangedPackages = new ArrayList<>();
+
+        /**
+         * {@code true} if one or more packages that contain {@link SearchableInfo} appeared.
+         */
+        private boolean mSearchablePackageAppeared = false;
 
         @Override
-        public void onSomePackagesChanged() {
-            updateSearchables();
+        public void onBeginPackageChanges() {
+            clearPackageChangeState();
         }
 
         @Override
-        public void onPackageModified(String pkg) {
-            updateSearchables();
+        public void onPackageAppeared(String packageName, int reason) {
+            if (!mSearchablePackageAppeared) {
+                // Check if the new appeared package contains SearchableInfo.
+                mSearchablePackageAppeared =
+                        hasSearchableForPackage(packageName, getChangingUserId());
+            }
+            mChangedPackages.add(packageName);
         }
 
-        private void updateSearchables() {
-            final int changingUserId = getChangingUserId();
+        @Override
+        public void onPackageDisappeared(String packageName, int reason) {
+            mChangedPackages.add(packageName);
+        }
+
+        @Override
+        public void onPackageModified(String packageName) {
+            mChangedPackages.add(packageName);
+        }
+
+        @Override
+        public void onFinishPackageChanges() {
+            onFinishPackageChangesInternal();
+            clearPackageChangeState();
+        }
+
+        private void clearPackageChangeState() {
+            mChangedPackages.clear();
+            mSearchablePackageAppeared = false;
+        }
+
+        private boolean hasSearchableForPackage(String packageName, int userId) {
+            final List<ResolveInfo> searchList = querySearchableActivities(mContext,
+                    new Intent(Intent.ACTION_SEARCH).setPackage(packageName), userId);
+            if (!searchList.isEmpty()) {
+                return true;
+            }
+
+            final List<ResolveInfo> webSearchList = querySearchableActivities(mContext,
+                    new Intent(Intent.ACTION_WEB_SEARCH).setPackage(packageName), userId);
+            if (!webSearchList.isEmpty()) {
+                return true;
+            }
+
+            final List<ResolveInfo> globalSearchList = querySearchableActivities(mContext,
+                    new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH).setPackage(packageName),
+                    userId);
+            return !globalSearchList.isEmpty();
+        }
+
+        private boolean shouldRebuildSearchableList(@UserIdInt int changingUserId) {
+            // This method is guaranteed to be called only on getRegisteredHandler()
+            if (mSearchablePackageAppeared) {
+                return true;
+            }
+
+            ArraySet<String> knownSearchablePackageNames = new ArraySet<>();
             synchronized (mSearchables) {
-                // Update list of searchable activities
-                for (int i = 0; i < mSearchables.size(); i++) {
-                    if (changingUserId == mSearchables.keyAt(i)) {
-                        mSearchables.valueAt(i).updateSearchableList();
-                        break;
-                    }
+                Searchables searchables = mSearchables.get(changingUserId);
+                if (searchables != null) {
+                    knownSearchablePackageNames = searchables.getKnownSearchablePackageNames();
                 }
             }
+
+            final int numOfPackages = mChangedPackages.size();
+            for (int i = 0; i < numOfPackages; i++) {
+                final String packageName = mChangedPackages.get(i);
+                if (knownSearchablePackageNames.contains(packageName)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private void onFinishPackageChangesInternal() {
+            final int changingUserId = getChangingUserId();
+            if (!shouldRebuildSearchableList(changingUserId)) {
+                return;
+            }
+
+            synchronized (mSearchables) {
+                // Invalidate the searchable list.
+                Searchables searchables = mSearchables.get(changingUserId);
+                if (searchables != null) {
+                    searchables.invalidateSearchableList();
+                }
+            }
+
             // Inform all listeners that the list of searchables has been updated.
             Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -180,6 +247,17 @@
         }
     }
 
+    @NonNull
+    static List<ResolveInfo> querySearchableActivities(Context context, Intent searchIntent,
+            @UserIdInt int userId) {
+        final List<ResolveInfo> activities = context.getPackageManager()
+                .queryIntentActivitiesAsUser(searchIntent, PackageManager.GET_META_DATA
+                        | PackageManager.MATCH_INSTANT
+                        | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+        return activities;
+    }
+
+
     class GlobalSearchProviderObserver extends ContentObserver {
         private final ContentResolver mResolver;
 
@@ -196,7 +274,7 @@
         public void onChange(boolean selfChange) {
             synchronized (mSearchables) {
                 for (int i = 0; i < mSearchables.size(); i++) {
-                    mSearchables.valueAt(i).updateSearchableList();
+                    mSearchables.valueAt(i).invalidateSearchableList();
                 }
             }
             Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 7b39775..dc67339 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -35,8 +35,10 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
 
 import java.io.FileDescriptor;
@@ -62,7 +64,6 @@
     private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*";
 
     private Context mContext;
-
     private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
     private ArrayList<SearchableInfo> mSearchablesList = null;
     private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
@@ -81,6 +82,12 @@
     final private IPackageManager mPm;
     // User for which this Searchables caches information
     private int mUserId;
+    @GuardedBy("this")
+    private boolean mRebuildSearchables = true;
+
+    // Package names that are known to contain {@link SearchableInfo}
+    @GuardedBy("this")
+    private ArraySet<String> mKnownSearchablePackageNames = new ArraySet<>();
 
     /**
      *
@@ -224,7 +231,14 @@
      *
      * TODO: sort the list somehow?  UI choice.
      */
-    public void updateSearchableList() {
+    public void updateSearchableListIfNeeded() {
+        synchronized (this) {
+            if (!mRebuildSearchables) {
+                // The searchable list is valid, no need to rebuild.
+                return;
+            }
+        }
+
         // These will become the new values at the end of the method
         HashMap<ComponentName, SearchableInfo> newSearchablesMap
                                 = new HashMap<ComponentName, SearchableInfo>();
@@ -232,6 +246,7 @@
                                 = new ArrayList<SearchableInfo>();
         ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                 = new ArrayList<SearchableInfo>();
+        ArraySet<String> newKnownSearchablePackageNames = new ArraySet<>();
 
         // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
         List<ResolveInfo> searchList;
@@ -264,6 +279,7 @@
                                 mUserId);
                         if (searchable != null) {
                             newSearchablesList.add(searchable);
+                            newKnownSearchablePackageNames.add(ai.packageName);
                             newSearchablesMap.put(searchable.getSearchActivity(), searchable);
                             if (searchable.shouldIncludeInGlobalSearch()) {
                                 newSearchablesInGlobalSearchList.add(searchable);
@@ -286,16 +302,41 @@
             synchronized (this) {
                 mSearchablesMap = newSearchablesMap;
                 mSearchablesList = newSearchablesList;
+                mKnownSearchablePackageNames = newKnownSearchablePackageNames;
                 mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
                 mGlobalSearchActivities = newGlobalSearchActivities;
                 mCurrentGlobalSearchActivity = newGlobalSearchActivity;
                 mWebSearchActivity = newWebSearchActivity;
+                for (ResolveInfo globalSearchActivity: mGlobalSearchActivities) {
+                    mKnownSearchablePackageNames.add(
+                            globalSearchActivity.getComponentInfo().packageName);
+                }
+                if (mCurrentGlobalSearchActivity != null) {
+                    mKnownSearchablePackageNames.add(
+                            mCurrentGlobalSearchActivity.getPackageName());
+                }
+                if (mWebSearchActivity != null) {
+                    mKnownSearchablePackageNames.add(mWebSearchActivity.getPackageName());
+                }
+
+                mRebuildSearchables = false;
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
+    synchronized ArraySet<String> getKnownSearchablePackageNames() {
+        return mKnownSearchablePackageNames;
+    }
+
+    synchronized void invalidateSearchableList() {
+        mRebuildSearchables = true;
+
+        // Don't rebuild the searchable list, it will be rebuilt
+        // when the next updateSearchableList gets called.
+    }
+
     /**
      * Returns a sorted list of installed search providers as per
      * the following heuristics:
@@ -532,6 +573,8 @@
                     pw.print("  "); pw.println(info.getSuggestAuthority());
                 }
             }
+
+            pw.println("mRebuildSearchables = " + mRebuildSearchables);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 7163319..5d17884 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.vibrator.V1_0.EffectStrength;
-import android.os.IExternalVibratorService;
+import android.os.ExternalVibrationScale;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -37,11 +37,13 @@
 
     // Scale levels. Each level, except MUTE, is defined as the delta between the current setting
     // and the default intensity for that type of vibration (i.e. current - default).
-    private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
-    private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
-    private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
-    private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
-    private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
+    private static final int SCALE_VERY_LOW =
+            ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
+    private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
+    private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
+    private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
+    private static final int SCALE_VERY_HIGH =
+            ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
 
     // Scale factors for each level.
     private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
@@ -83,9 +85,9 @@
      * Calculates the scale to be applied to external vibration with given usage.
      *
      * @param usageHint one of VibrationAttributes.USAGE_*
-     * @return one of IExternalVibratorService.SCALE_*
+     * @return one of ExternalVibrationScale.ScaleLevel.SCALE_*
      */
-    public int getExternalVibrationScale(int usageHint) {
+    public int getExternalVibrationScaleLevel(int usageHint) {
         int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
         int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
 
@@ -107,6 +109,22 @@
     }
 
     /**
+     * Returns the adaptive haptics scale that should be applied to the vibrations with
+     * the given usage. When no adaptive scales are available for the usages, then returns 1
+     * indicating no scaling will be applied
+     *
+     * @param usageHint one of VibrationAttributes.USAGE_*
+     * @return The adaptive haptics scale.
+     */
+    public float getAdaptiveHapticsScale(int usageHint) {
+        if (shouldApplyAdaptiveHapticsScale(usageHint)) {
+            return mAdaptiveHapticsScales.get(usageHint);
+        }
+
+        return 1f; // no scaling
+    }
+
+    /**
      * Scale a {@link VibrationEffect} based on the given usage hint for this vibration.
      *
      * @param effect    the effect to be scaled
@@ -152,9 +170,7 @@
             }
 
             // If adaptive haptics scaling is available for this usage, apply it to the segment.
-            if (Flags.adaptiveHapticsEnabled()
-                    && mAdaptiveHapticsScales.size() > 0
-                    && mAdaptiveHapticsScales.contains(usageHint)) {
+            if (shouldApplyAdaptiveHapticsScale(usageHint)) {
                 float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
                 segment = segment.scaleLinearly(adaptiveScale);
             }
@@ -224,6 +240,10 @@
         mAdaptiveHapticsScales.clear();
     }
 
+    private boolean shouldApplyAdaptiveHapticsScale(int usageHint) {
+        return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint);
+    }
+
     /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
     private static int intensityToEffectStrength(int intensity) {
         switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index be5d158..78e0ebb 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
 import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
 import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
 
@@ -35,6 +36,7 @@
 import android.os.Build;
 import android.os.CombinedVibration;
 import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IExternalVibratorService;
@@ -277,7 +279,7 @@
         context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
 
         injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
-        if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
+        if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) {
             injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService);
         }
 
@@ -1427,6 +1429,10 @@
         VibratorControllerHolder createVibratorControllerHolder() {
             return new VibratorControllerHolder();
         }
+
+        boolean isServiceDeclared(String name) {
+            return ServiceManager.isDeclared(name);
+        }
     }
 
     /**
@@ -1594,7 +1600,7 @@
             IBinder.DeathRecipient {
 
         public final ExternalVibration externalVibration;
-        public int scale;
+        public ExternalVibrationScale scale = new ExternalVibrationScale();
 
         private Vibration.Status mStatus;
 
@@ -1605,7 +1611,6 @@
                     // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
                     Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
             this.externalVibration = externalVibration;
-            this.scale = IExternalVibratorService.SCALE_NONE;
             mStatus = Vibration.Status.RUNNING;
         }
 
@@ -1658,7 +1663,7 @@
 
         public Vibration.DebugInfo getDebugInfo() {
             return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
-                    /* originalEffect= */ null, scale, callerInfo);
+                    /* originalEffect= */ null, scale.scaleLevel, callerInfo);
         }
 
         public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1988,11 +1993,17 @@
     /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
     @VisibleForTesting
     final class ExternalVibratorService extends IExternalVibratorService.Stub {
+        private static final ExternalVibrationScale SCALE_MUTE = new ExternalVibrationScale();
+
+        static {
+            SCALE_MUTE.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+        }
 
         @Override
-        public int onExternalVibrationStart(ExternalVibration vib) {
+        public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
+
             if (!hasExternalControlCapability()) {
-                return IExternalVibratorService.SCALE_MUTE;
+                return SCALE_MUTE;
             }
             if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
                     vib.getUid(), -1 /*owningUid*/, true /*exported*/)
@@ -2000,7 +2011,7 @@
                 Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
                         + " tried to play externally controlled vibration"
                         + " without VIBRATE permission, ignoring.");
-                return IExternalVibratorService.SCALE_MUTE;
+                return SCALE_MUTE;
             }
 
             // Create Vibration.Stats as close to the received request as possible, for tracking.
@@ -2033,7 +2044,7 @@
                 }
 
                 if (vibrationEndInfo != null) {
-                    vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
+                    vibHolder.scale = SCALE_MUTE;
                     // Failed to start the vibration, end it and report metrics right away.
                     endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
                     return vibHolder.scale;
@@ -2074,7 +2085,10 @@
                 }
                 mCurrentExternalVibration = vibHolder;
                 vibHolder.linkToDeath();
-                vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage());
+                vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel(
+                        attrs.getUsage());
+                vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale(
+                        attrs.getUsage());
             }
 
             if (waitForCompletion) {
@@ -2086,7 +2100,7 @@
                                 new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
                                 /* continueExternalControl= */ false);
                     }
-                    return IExternalVibratorService.SCALE_MUTE;
+                    return SCALE_MUTE;
                 }
             }
             if (!alreadyUnderExternalControl) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index b3c8b0b..27c80c4 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -107,15 +107,20 @@
         mContext = context;
         mSystemInterface = systemInterface;
         WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+
+        WebViewProviderInfo defaultProvider = null;
         for (WebViewProviderInfo provider : webviewProviders) {
             if (provider.availableByDefault) {
-                mDefaultProvider = provider;
+                defaultProvider = provider;
                 break;
             }
         }
-        // This should be unreachable because the config parser enforces that there is at least one
-        // availableByDefault provider.
-        throw new AndroidRuntimeException("No available by default WebView Provider.");
+        if (defaultProvider == null) {
+            // This should be unreachable because the config parser enforces that there is at least
+            // one availableByDefault provider.
+            throw new AndroidRuntimeException("No available by default WebView Provider.");
+        }
+        mDefaultProvider = defaultProvider;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 56024f7..bc6f93f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1535,11 +1535,12 @@
             // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
             // update that here in order. Set the last reported MW state to the same as the PiP
             // state since we haven't yet actually resized the task (these callbacks need to
-            // precede the configuration change from the resize.
+            // precede the configuration change from the resize.)
             mLastReportedPictureInPictureMode = inPictureInPictureMode;
             mLastReportedMultiWindowMode = inPictureInPictureMode;
             ensureActivityConfiguration(true /* ignoreVisibility */);
-            if (inPictureInPictureMode && findMainWindow() == null) {
+            if (inPictureInPictureMode && findMainWindow() == null
+                    && task.topRunningActivity() == this) {
                 // Prevent malicious app entering PiP without valid WindowState, which can in turn
                 // result a non-touchable PiP window since the InputConsumer for PiP requires it.
                 EventLog.writeEvent(0x534e4554, "265293293", -1, "");
@@ -3549,7 +3550,7 @@
             IBinder callerToken = new Binder();
             if (android.security.Flags.contentUriPermissionApis()) {
                 try {
-                    resultTo.computeCallerInfo(callerToken, intent, this.getUid(),
+                    resultTo.computeCallerInfo(callerToken, resultData, this.getUid(),
                             mAtmService.getPackageManager().getNameForUid(this.getUid()),
                             /* isShareIdentityEnabled */ false);
                     // Result callers cannot share their identity via
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index a469165..b38a2f9 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -117,6 +117,9 @@
                 <xs:element type="sensorDetails" name="proxSensor">
                     <xs:annotation name="final"/>
                 </xs:element>
+                <xs:element type="sensorDetails" name="tempSensor">
+                    <xs:annotation name="final"/>
+                </xs:element>
 
                 <!-- Length of the ambient light horizon used to calculate the long & short term
                 estimates of ambient light in milliseconds.-->
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 79ea274..b329db4 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -133,6 +133,7 @@
     method public final java.math.BigDecimal getScreenBrightnessRampSlowIncreaseIdle();
     method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
     method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
+    method public final com.android.server.display.config.SensorDetails getTempSensor();
     method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
     method public final com.android.server.display.config.UsiVersion getUsiVersion();
     method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
@@ -167,6 +168,7 @@
     method public final void setScreenBrightnessRampSlowIncreaseIdle(java.math.BigDecimal);
     method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
     method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
+    method public final void setTempSensor(com.android.server.display.config.SensorDetails);
     method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
     method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
   }
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index b7af58c..3bce9b5 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -69,7 +69,7 @@
 }
 
 android_ravenwood_test {
-    name: "FrameworksInputMethodSystemServerTests_host",
+    name: "FrameworksInputMethodSystemServerTestsRavenwood",
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.test.rules",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
index 8faaf59..05c243f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -43,6 +43,7 @@
 import com.android.server.display.BrightnessThrottler.Injector;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
 import com.android.server.display.mode.DisplayModeDirectorTest;
 
 import org.junit.Before;
@@ -56,6 +57,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -292,6 +294,53 @@
         assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
     }
 
+
+    @Test
+    public void testThermalThrottlingWithDisplaySensor() throws Exception {
+        final ThrottlingLevel level =
+                    new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f);
+        List<ThrottlingLevel> levels = new ArrayList<>(List.of(level));
+        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
+        final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+        final BrightnessThrottler throttler =
+                    createThrottlerSupportedWithTempSensor(data, tempSensor);
+        assertTrue(throttler.deviceSupportsThrottling());
+
+        verify(mThermalServiceMock)
+                    .registerThermalEventListenerWithType(
+                        mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_DISPLAY));
+        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+        // Set VIRTUAL-SKIN-DISPLAY tatus too low to verify no throttling.
+        listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus - 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+        // Verify when skin sensor throttled, no brightness throttling triggered.
+        listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+        // Verify when display sensor of another name throttled, no brightness throttling triggered.
+        listener.notifyThrottling(getDisplayTempWithName("ANOTHER-NAME", level.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+        // Verify when display sensor of current name throttled, brightness throttling triggered.
+        listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+                throttler.getBrightnessMaxReason());
+    }
+
     @Test public void testUpdateThermalThrottlingData() throws Exception {
         // Initialise brightness throttling levels
         // Ensure that they are overridden by setting the data through device config.
@@ -476,18 +525,30 @@
         return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
                 /* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
                 /* thermalThrottlingDataId= */ null,
-                /* thermalThrottlingDataMap= */ new HashMap<>(1));
+                /* thermalThrottlingDataMap= */ new HashMap<>(1),
+                /* tempSensor= */ null);
     }
 
     private BrightnessThrottler createThrottlerSupported(ThermalBrightnessThrottlingData data) {
+        SensorData tempSensor = SensorData.loadTempSensorUnspecifiedConfig();
+        return createThrottlerSupportedWithTempSensor(data, tempSensor);
+    }
+    private BrightnessThrottler createThrottlerSupportedWithTempSensor(
+                ThermalBrightnessThrottlingData data, SensorData tempSensor) {
         assertNotNull(data);
-        HashMap<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
+        Map<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
         throttlingDataMap.put("default", data);
         return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
-                () -> {}, "123", "default", throttlingDataMap);
+                    () -> {}, "123", "default", throttlingDataMap, tempSensor);
     }
 
     private Temperature getSkinTemp(@ThrottlingStatus int status) {
         return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
     }
+
+    private Temperature getDisplayTempWithName(
+                String sensorName, @ThrottlingStatus int status) {
+        assertNotNull(sensorName);
+        return new Temperature(30.0f, Temperature.TYPE_DISPLAY, sensorName, status);
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index b29fc88..2867041 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -20,6 +20,7 @@
 import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
 import static com.android.server.display.config.SensorData.SupportedMode;
 import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
 import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -106,6 +107,7 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getResources()).thenReturn(mResources);
         when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+        when(mFlags.isSensorBasedBrightnessThrottlingEnabled()).thenReturn(true);
         mockDeviceConfigs();
     }
 
@@ -143,6 +145,8 @@
         assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
         assertNull(mDisplayDeviceConfig.getProximitySensor().type);
         assertNull(mDisplayDeviceConfig.getProximitySensor().name);
+        assertEquals(TEMPERATURE_TYPE_SKIN, mDisplayDeviceConfig.getTempSensor().type);
+        assertNull(mDisplayDeviceConfig.getTempSensor().name);
         assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
     }
 
@@ -592,6 +596,13 @@
     }
 
     @Test
+    public void testTempSensorFromDisplayConfig() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+        assertEquals("DISPLAY", mDisplayDeviceConfig.getTempSensor().type);
+        assertEquals("VIRTUAL-SKIN-DISPLAY", mDisplayDeviceConfig.getTempSensor().name);
+    }
+
+    @Test
     public void testBlockingZoneThresholdsFromDisplayConfig() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile();
 
@@ -1356,6 +1367,10 @@
                 +       "<name>Test Binned Brightness Sensor</name>\n"
                 +   "</screenOffBrightnessSensor>\n"
                 + proxSensor
+                +   "<tempSensor>\n"
+                +       "<type>DISPLAY</type>\n"
+                +       "<name>VIRTUAL-SKIN-DISPLAY</name>\n"
+                +   "</tempSensor>\n"
                 +   "<ambientBrightnessChangeThresholds>\n"
                 +       "<brighteningThresholds>\n"
                 +           "<minimum>10</minimum>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
index 37d0f62..34f352e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
@@ -37,10 +37,14 @@
 import com.android.server.display.DisplayDeviceConfig;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.testutils.FakeDeviceConfigInterface;
 import com.android.server.testutils.TestHandler;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,9 +54,6 @@
 
 import java.util.List;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 @RunWith(JUnitParamsRunner.class)
 public class BrightnessThermalClamperTest {
 
@@ -125,13 +126,13 @@
     public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
             @Temperature.ThrottlingStatus int throttlingStatus,
             boolean expectedActive, float expectedBrightness) throws RemoteException {
-        IThermalEventListener thermalEventListener = captureThermalEventListener();
+        IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
         mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
         mTestHandler.flush();
         assertFalse(mClamper.isActive());
         assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
 
-        thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+        thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
         mTestHandler.flush();
         assertEquals(expectedActive, mClamper.isActive());
         assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -139,11 +140,11 @@
 
     @Test
     @Parameters(method = "testThrottlingData")
-    public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels,
+    public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
             @Temperature.ThrottlingStatus int throttlingStatus,
             boolean expectedActive, float expectedBrightness) throws RemoteException {
-        IThermalEventListener thermalEventListener = captureThermalEventListener();
-        thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+        IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+        thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
         mTestHandler.flush();
         assertFalse(mClamper.isActive());
         assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -156,8 +157,8 @@
 
     @Test
     public void testOverrideData() throws RemoteException {
-        IThermalEventListener thermalEventListener = captureThermalEventListener();
-        thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
+        IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+        thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
         mTestHandler.flush();
         assertFalse(mClamper.isActive());
         assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -183,15 +184,60 @@
         assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
     }
 
-    private IThermalEventListener captureThermalEventListener() throws RemoteException {
+    @Test
+    public void testDisplaySensorBasedThrottling() throws RemoteException {
+        final int severity = PowerManager.THERMAL_STATUS_SEVERE;
+        IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+        // Update config to listen to display type sensor.
+        final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+        final TestThermalData thermalData =
+                    new TestThermalData(
+                        DISPLAY_ID,
+                        DisplayDeviceConfig.DEFAULT_ID,
+                        List.of(new ThrottlingLevel(severity, 0.5f)),
+                        tempSensor);
+        mClamper.onDisplayChanged(thermalData);
+        mTestHandler.flush();
+        verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
+        thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
+        assertFalse(mClamper.isActive());
+
+        // Verify no throttling triggered when any other sensor notification received.
+        thermalEventListener.notifyThrottling(createSkinTemperature(severity));
+        mTestHandler.flush();
+        assertFalse(mClamper.isActive());
+
+        thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
+        mTestHandler.flush();
+        assertFalse(mClamper.isActive());
+
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+        // Verify throttling triggered when display sensor of given name throttled.
+        thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
+        mTestHandler.flush();
+        assertTrue(mClamper.isActive());
+        assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+    }
+
+    private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
+        return captureThermalEventListener(Temperature.TYPE_SKIN);
+    }
+
+    private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
         ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
                 IThermalEventListener.class);
         verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
-                Temperature.TYPE_SKIN));
+                type));
         return captor.getValue();
     }
 
-    private Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
+    private Temperature createDisplayTemperature(
+                @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
+        return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
+    }
+
+    private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
         return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
     }
 
@@ -217,19 +263,26 @@
         private final String mUniqueDisplayId;
         private final String mDataId;
         private final ThermalBrightnessThrottlingData mData;
+        private final SensorData mTempSensor;
 
         private TestThermalData() {
-            this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null);
+            this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null,
+                    SensorData.loadTempSensorUnspecifiedConfig());
         }
 
         private TestThermalData(List<ThrottlingLevel> data) {
-            this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data);
+            this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data,
+                    SensorData.loadTempSensorUnspecifiedConfig());
         }
-        private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) {
+
+        private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data,
+                    SensorData tempSensor) {
             mUniqueDisplayId = uniqueDisplayId;
             mDataId = dataId;
             mData = ThermalBrightnessThrottlingData.create(data);
+            mTempSensor = tempSensor;
         }
+
         @NonNull
         @Override
         public String getUniqueDisplayId() {
@@ -247,5 +300,11 @@
         public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
             return mData;
         }
+
+        @NonNull
+        @Override
+        public SensorData getTempSensor() {
+            return mTempSensor;
+        }
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 940469f..414532b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -29,16 +29,20 @@
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupTransport;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.Build;
 import android.os.Message;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.modules.utils.testing.TestableDeviceConfig;
+import com.android.server.backup.Flags;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.BackupHandler;
 import com.android.server.backup.transport.BackupTransportClient;
@@ -56,10 +60,12 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
@@ -74,10 +80,17 @@
     private static final String SYSTEM_PACKAGE_NAME = "android";
     private static final String NON_SYSTEM_PACKAGE_NAME = "package";
 
-    @Mock private BackupDataInput mBackupDataInput;
-    @Mock private BackupDataOutput mBackupDataOutput;
-    @Mock private UserBackupManagerService mBackupManagerService;
-    @Mock private TransportConnection mTransportConnection;
+    private static final String V_TO_U_ALLOWLIST = "pkg1";
+    private static final String V_TO_U_DENYLIST = "pkg2";
+
+    @Mock
+    private BackupDataInput mBackupDataInput;
+    @Mock
+    private BackupDataOutput mBackupDataOutput;
+    @Mock
+    private UserBackupManagerService mBackupManagerService;
+    @Mock
+    private TransportConnection mTransportConnection;
 
     private Set<String> mExcludedkeys = new HashSet<>();
     private Map<String, String> mBackupData = new HashMap<>();
@@ -91,6 +104,10 @@
     public TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule =
             new TestableDeviceConfig.TestableDeviceConfigRule();
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
     private Context mContext;
 
     @Before
@@ -118,7 +135,8 @@
                                     return null;
                                 });
 
-        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
+        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection,
+                V_TO_U_ALLOWLIST, V_TO_U_DENYLIST);
     }
 
     private void populateTestData() {
@@ -235,6 +253,122 @@
                         == UnifiedRestoreState.FINAL);
     }
 
+    @Test
+    public void testCreateVToUList_listSettingIsNull_returnEmptyList() {
+        List<String> expectedEmptyList = new ArrayList<>();
+
+        List<String> list = mRestoreTask.createVToUList(null);
+
+        assertEquals(list, expectedEmptyList);
+    }
+
+    @Test
+    public void testCreateVToUList_listIsNotNull_returnCorrectList() {
+        List<String> expectedList = Arrays.asList("a", "b", "c");
+        String listString = "a,b,c";
+
+        List<String> list = mRestoreTask.createVToUList(listString);
+
+        assertEquals(list, expectedList);
+    }
+
+    @Test
+    public void testIsVToUDowngrade_vToUFlagIsOffAndTargetIsUSourceIsV_returnFalse() {
+        mSetFlagsRule.disableFlags(
+                Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+        boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+                Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+        assertFalse(isVToUDowngrade);
+    }
+
+    @Test
+    public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsUSourceIsV_returnTrue() {
+        mSetFlagsRule.enableFlags(
+                Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+        boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+                Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+        assertTrue(isVToUDowngrade);
+    }
+
+    @Test
+    public void testIsVToUDowngrade_vToUFlagIsOnAndSourceIsNotV_returnFalse() {
+        mSetFlagsRule.enableFlags(
+                Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+        boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+                Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+        assertFalse(isVToUDowngrade);
+    }
+
+    @Test
+    public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsNotU_returnFalse() {
+        mSetFlagsRule.enableFlags(
+                Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+        boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+                Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+        assertFalse(isVToUDowngrade);
+    }
+
+
+    @Test
+    public void testIsEligibleForVToUDowngrade_pkgIsNotOnAllowlist_returnFalse() {
+        PackageInfo testPackageInfo = new PackageInfo();
+        testPackageInfo.packageName = "pkg";
+        testPackageInfo.applicationInfo = new ApplicationInfo();
+        // restoreAnyVersion flag is off
+        testPackageInfo.applicationInfo.flags = 0;
+
+        boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+        assertFalse(eligibilityCriteria);
+    }
+
+    @Test
+    public void testIsEligibleForVToUDowngrade_pkgIsOnAllowlist_returnTrue() {
+        PackageInfo testPackageInfo = new PackageInfo();
+        testPackageInfo.packageName = "pkg1";
+        testPackageInfo.applicationInfo = new ApplicationInfo();
+        // restoreAnyVersion flag is off
+        testPackageInfo.applicationInfo.flags = 0;
+
+        boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+        assertTrue(eligibilityCriteria);
+    }
+
+    @Test
+    public void testIsEligibleForVToUDowngrade_pkgIsNotOnDenyList_returnTrue() {
+        PackageInfo testPackageInfo = new PackageInfo();
+        testPackageInfo.packageName = "pkg";
+        testPackageInfo.applicationInfo = new ApplicationInfo();
+        // restoreAnyVersion flag is on
+        testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+        boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+        assertTrue(eligibilityCriteria);
+    }
+
+    @Test
+    public void testIsEligibleForVToUDowngrade_pkgIsOnDenyList_returnFalse() {
+        PackageInfo testPackageInfo = new PackageInfo();
+        testPackageInfo.packageName = "pkg2";
+        testPackageInfo.applicationInfo = new ApplicationInfo();
+        // restoreAnyVersion flag is on
+        testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+        boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+        assertFalse(eligibilityCriteria);
+    }
+
     private void setupForRestoreKeyValueState(int transportStatus)
             throws RemoteException, TransportNotAvailableException {
         // Mock BackupHandler to do nothing when executeNextState() is called
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index f5c6795..771a765 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -92,7 +92,7 @@
     public void testNonSearchable() {
         // test basic array & hashmap
         Searchables searchables = new Searchables(mContext, 0);
-        searchables.updateSearchableList();
+        searchables.updateSearchableListIfNeeded();
 
         // confirm that we return null for non-searchy activities
         ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests",
@@ -121,7 +121,7 @@
         doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
 
         Searchables searchables = new Searchables(mContext, 0);
-        searchables.updateSearchableList();
+        searchables.updateSearchableListIfNeeded();
         // tests with "real" searchables (deprecate, this should be a unit test)
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         int count = searchablesList.size();
@@ -139,7 +139,7 @@
         doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
 
         Searchables searchables = new Searchables(mContext, 0);
-        searchables.updateSearchableList();
+        searchables.updateSearchableListIfNeeded();
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         assertNotNull(searchablesList);
         MoreAsserts.assertEmpty(searchablesList);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cff7f46..715c9d4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -12008,7 +12008,7 @@
 
         // style + self managed call - bypasses block
         when(mTelecomManager.isInSelfManagedCall(
-                r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true);
+                r.getSbn().getPackageName(), true)).thenReturn(true);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
                 r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
 
@@ -12091,7 +12091,7 @@
         // style + self managed call - bypasses block
         mService.clearNotifications();
         reset(mUsageStats);
-        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true))
+        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), true))
                 .thenReturn(true);
 
         mService.addEnqueuedNotification(r);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index b431888..3e59878 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -35,8 +35,8 @@
 import android.content.ContentResolver;
 import android.content.ContextWrapper;
 import android.content.pm.PackageManagerInternal;
+import android.os.ExternalVibrationScale;
 import android.os.Handler;
-import android.os.IExternalVibratorService;
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
@@ -49,6 +49,7 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -119,29 +120,65 @@
     public void testGetExternalVibrationScale() {
         setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
-        assertEquals(IExternalVibratorService.SCALE_VERY_HIGH,
-                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH,
+                mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
 
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
-        assertEquals(IExternalVibratorService.SCALE_HIGH,
-                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH,
+                mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
 
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
-        assertEquals(IExternalVibratorService.SCALE_NONE,
-                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
+                mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
 
         setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
-        assertEquals(IExternalVibratorService.SCALE_LOW,
-                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW,
+                mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
 
         setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
-        assertEquals(IExternalVibratorService.SCALE_VERY_LOW,
-                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW,
+                mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
 
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         // Vibration setting being bypassed will use default setting and not scale.
-        assertEquals(IExternalVibratorService.SCALE_NONE,
-                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
+                mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    public void testAdaptiveHapticsScale_withAdaptiveHapticsAvailable() {
+        setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+
+        mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
+        mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
+
+        assertEquals(0.5f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
+        assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+        assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
+
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+        // Vibration setting being bypassed will apply adaptive haptics scales.
+        assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    public void testAdaptiveHapticsScale_flagDisabled_adaptiveHapticScaleAlwaysNone() {
+        setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+
+        mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
+        mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
+
+        assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
+        assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+        assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
     }
 
     @Test
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 2823223..417fbd0 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,6 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManagerInternal;
 import android.frameworks.vibrator.ScaleParam;
-import android.frameworks.vibrator.VibrationParam;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -55,8 +54,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 
@@ -135,7 +132,7 @@
         vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
 
         mVibratorControlService.onRequestVibrationParamsComplete(token,
-                generateVibrationParams(vibrationScales));
+                VibrationParamGenerator.generateVibrationParams(vibrationScales));
 
         verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f);
         verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_NOTIFICATION, 0.4f);
@@ -162,7 +159,7 @@
         vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
 
         mVibratorControlService.onRequestVibrationParamsComplete(new Binder(),
-                generateVibrationParams(vibrationScales));
+                VibrationParamGenerator.generateVibrationParams(vibrationScales));
 
         verifyZeroInteractions(mMockVibrationScaler);
     }
@@ -175,7 +172,8 @@
         vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
         vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
 
-        mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+        mVibratorControlService.setVibrationParams(
+                VibrationParamGenerator.generateVibrationParams(vibrationScales),
                 mFakeVibratorController);
 
         verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f);
@@ -193,7 +191,8 @@
         vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
         vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
 
-        mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+        mVibratorControlService.setVibrationParams(
+                VibrationParamGenerator.generateVibrationParams(vibrationScales),
                 mFakeVibratorController);
 
         verifyZeroInteractions(mMockVibrationScaler);
@@ -268,28 +267,6 @@
         }
     }
 
-    private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
-        List<VibrationParam> vibrationParamList = new ArrayList<>();
-        for (int i = 0; i < vibrationScales.size(); i++) {
-            int type = vibrationScales.keyAt(i);
-            float scale = vibrationScales.valueAt(i);
-
-            vibrationParamList.add(generateVibrationParam(type, scale));
-        }
-
-        return vibrationParamList.toArray(new VibrationParam[0]);
-    }
-
-    private VibrationParam generateVibrationParam(int type, float scale) {
-        ScaleParam scaleParam = new ScaleParam();
-        scaleParam.typesMask = type;
-        scaleParam.scale = scale;
-        VibrationParam vibrationParam = new VibrationParam();
-        vibrationParam.setScale(scaleParam);
-
-        return vibrationParam;
-    }
-
     private int buildVibrationTypesMask(int... types) {
         int typesMask = 0;
         for (int type : types) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index e7571ef..ed89ccf 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -50,6 +50,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.res.Resources;
+import android.frameworks.vibrator.ScaleParam;
 import android.hardware.input.IInputManager;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerGlobal;
@@ -59,16 +60,17 @@
 import android.media.AudioManager;
 import android.os.CombinedVibration;
 import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IExternalVibrationController;
-import android.os.IExternalVibratorService;
 import android.os.IVibratorStateListener;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
@@ -82,6 +84,10 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.util.SparseArray;
@@ -153,6 +159,8 @@
     public MockitoRule rule = MockitoJUnit.rule();
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
@@ -185,8 +193,10 @@
     private Context mContextSpy;
     private TestLooper mTestLooper;
     private FakeVibrator mVibrator;
+    private FakeVibratorController mFakeVibratorController;
     private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
     private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
+    private VibratorControlService mVibratorControlService;
     private VibrationConfig mVibrationConfig;
     private InputManagerGlobal.TestSession mInputManagerGlobalSession;
     private InputManager mInputManager;
@@ -197,6 +207,7 @@
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
         mVibrationConfig = new VibrationConfig(mContextSpy.getResources());
+        mFakeVibratorController = new FakeVibratorController();
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
@@ -310,6 +321,8 @@
                         if (service instanceof VibratorManagerService.ExternalVibratorService) {
                             mExternalVibratorService =
                                     (VibratorManagerService.ExternalVibratorService) service;
+                        } else if (service instanceof VibratorControlService) {
+                            mVibratorControlService = (VibratorControlService) service;
                         }
                     }
 
@@ -321,9 +334,13 @@
 
                     VibratorControllerHolder createVibratorControllerHolder() {
                         VibratorControllerHolder holder = new VibratorControllerHolder();
-                        holder.setVibratorController(new FakeVibratorController());
+                        holder.setVibratorController(mFakeVibratorController);
                         return holder;
                     }
+
+                    boolean isServiceDeclared(String name) {
+                        return true;
+                    }
                 });
         return mService;
     }
@@ -1108,12 +1125,13 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS,
                 controller, firstToken);
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
 
         vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                 RINGTONE_ATTRS);
 
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
         // The external vibration should have been cancelled
         verify(controller).mute();
         assertEquals(Arrays.asList(false, true, false),
@@ -1708,13 +1726,14 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS,
                 mock(IExternalVibrationController.class), binderToken);
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID))
                 .thenReturn(true);
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
     }
 
     @Test
@@ -1727,10 +1746,11 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS,
                 mock(IExternalVibrationController.class), binderToken);
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
         mExternalVibratorService.onExternalVibrationStop(externalVibration);
 
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
         assertEquals(Arrays.asList(false, true, false),
                 mVibratorProviders.get(1).getExternalControlStates());
 
@@ -1753,17 +1773,19 @@
         ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS,
                 firstController, firstToken);
-        int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration);
+        ExternalVibrationScale firstScale =
+                mExternalVibratorService.onExternalVibrationStart(firstVibration);
 
         AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
                 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
                 .build();
         ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 ringtoneAudioAttrs, secondController, secondToken);
-        int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
+        ExternalVibrationScale secondScale =
+                mExternalVibratorService.onExternalVibrationStart(secondVibration);
 
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale);
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, firstScale.scaleLevel);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, secondScale.scaleLevel);
         verify(firstController).mute();
         verify(secondController, never()).mute();
         // Set external control called only once.
@@ -1799,8 +1821,9 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS,
                 mock(IExternalVibrationController.class));
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         // Vibration is cancelled.
         assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1825,9 +1848,10 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS,
                 mock(IExternalVibrationController.class));
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
         // External vibration is ignored.
-        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         // Vibration is not cancelled.
         assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
@@ -1852,8 +1876,9 @@
 
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class));
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         // Vibration is cancelled.
         assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1879,9 +1904,10 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
                 AUDIO_NOTIFICATION_ATTRS,
                 mock(IExternalVibrationController.class));
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
         // New vibration is ignored.
-        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         // Vibration is not cancelled.
         assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
@@ -1901,18 +1927,19 @@
 
         setRingerMode(AudioManager.RINGER_MODE_SILENT);
         createSystemReadyService();
-        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
     }
 
     @Test
@@ -1935,14 +1962,14 @@
 
         ExternalVibration vib = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
                 mock(IExternalVibrationController.class));
-        int scale = mExternalVibratorService.onExternalVibrationStart(vib);
-        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(vib);
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         mExternalVibratorService.onExternalVibrationStop(vib);
         scale = mExternalVibratorService.onExternalVibrationStart(
                 new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs,
                         mock(IExternalVibrationController.class)));
-        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
     }
 
     @Test
@@ -1956,10 +1983,91 @@
                 .build();
         createSystemReadyService();
 
-        int scale = mExternalVibratorService.onExternalVibrationStart(
-                new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs,
-                        mock(IExternalVibrationController.class)));
-        assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(
+                        new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs,
+                                mock(IExternalVibrationController.class)));
+        assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales()
+            throws RemoteException {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+                IVibrator.CAP_AMPLITUDE_CONTROL);
+        createSystemReadyService();
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(
+                VibrationParamGenerator.generateVibrationParams(vibrationScales),
+                mFakeVibratorController);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS,
+                mock(IExternalVibrationController.class));
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+        assertEquals(scale.adaptiveHapticsScale, 0.7f, 0);
+
+        externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_NOTIFICATION_ATTRS,
+                mock(IExternalVibrationController.class));
+        scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+        assertEquals(scale.adaptiveHapticsScale, 0.4f, 0);
+
+        AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+                .build();
+        externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                ringtoneAudioAttrs,
+                mock(IExternalVibrationController.class));
+        scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+        assertEquals(scale.adaptiveHapticsScale, 1f, 0);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone()
+            throws RemoteException {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+                IVibrator.CAP_AMPLITUDE_CONTROL);
+        createSystemReadyService();
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(
+                VibrationParamGenerator.generateVibrationParams(vibrationScales),
+                mFakeVibratorController);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS,
+                mock(IExternalVibrationController.class));
+        ExternalVibrationScale scale =
+                mExternalVibratorService.onExternalVibrationStart(externalVibration);
+        mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+        assertEquals(scale.adaptiveHapticsScale, 1f, 0);
+
+        mVibratorControlService.setVibrationParams(
+                VibrationParamGenerator.generateVibrationParams(vibrationScales),
+                mFakeVibratorController);
+        externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_NOTIFICATION_ATTRS,
+                mock(IExternalVibrationController.class));
+        scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+        assertEquals(scale.adaptiveHapticsScale, 1f, 0);
     }
 
     @Test
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
new file mode 100644
index 0000000..a606388
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class that can be used to generate arrays of {@link VibrationParam}.
+ */
+public final class VibrationParamGenerator {
+    /**
+     * Generates an array of {@link VibrationParam}.
+     */
+    public static VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
+        List<VibrationParam> vibrationParamList = new ArrayList<>();
+        for (int i = 0; i < vibrationScales.size(); i++) {
+            int type = vibrationScales.keyAt(i);
+            float scale = vibrationScales.valueAt(i);
+
+            vibrationParamList.add(generateVibrationParam(type, scale));
+        }
+
+        return vibrationParamList.toArray(new VibrationParam[0]);
+    }
+
+    private static VibrationParam generateVibrationParam(int type, float scale) {
+        ScaleParam scaleParam = new ScaleParam();
+        scaleParam.typesMask = type;
+        scaleParam.scale = scale;
+        VibrationParam vibrationParam = new VibrationParam();
+        vibrationParam.setScale(scaleParam);
+
+        return vibrationParam;
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index d84620b..ead36f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
 import android.view.SurfaceControl;
 import android.window.SurfaceSyncGroup;
 
@@ -370,6 +371,7 @@
         assertEquals(0, finishedLatch.getCount());
     }
 
+    @Test
     public void testSurfaceSyncGroupTimeout() throws InterruptedException {
         final CountDownLatch finishedLatch = new CountDownLatch(1);
         SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
@@ -386,7 +388,7 @@
 
         // Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little
         // over 1 second to make sure it completes.
-        finishedLatch.await(1100, TimeUnit.MILLISECONDS);
+        finishedLatch.await(1100L * BuildUtils.HW_TIMEOUT_MULTIPLIER, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch.getCount());
     }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 889f842..c217780 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2582,7 +2582,6 @@
                         if (anyPackagesAppearing()) {
                             initRecognizer(userHandle);
                         }
-                        return;
                     }
 
                     if (curInteractor != null) {
@@ -2631,15 +2630,16 @@
                         }
                     }
 
-                    // There is no interactor, so just deal with a simple recognizer.
-                    int change = isPackageDisappearing(curRecognizer.getPackageName());
-                    if (change == PACKAGE_PERMANENT_CHANGE
-                            || change == PACKAGE_TEMPORARY_CHANGE) {
-                        setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
+                    if (curRecognizer != null) {
+                        int change = isPackageDisappearing(curRecognizer.getPackageName());
+                        if (change == PACKAGE_PERMANENT_CHANGE
+                                || change == PACKAGE_TEMPORARY_CHANGE) {
+                            setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
 
-                    } else if (isPackageModified(curRecognizer.getPackageName())) {
-                        setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
-                                userHandle), userHandle);
+                        } else if (isPackageModified(curRecognizer.getPackageName())) {
+                            setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
+                                    userHandle), userHandle);
+                        }
                     }
                 }
             }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 08c76af..9792cdd 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2797,13 +2797,10 @@
      * calls for a given {@code packageName} and {@code userHandle}.
      *
      * @param packageName the package name of the app to check calls for.
-     * @param userHandle the user handle on which to check for calls.
-     * @param detectForAllUsers indicates if calls should be detected across all users. If it is
-     *                          set to {@code true}, and the caller has the ability to interact
-     *                          across users, the userHandle parameter is disregarded.
+     * @param userHandle the user handle to check calls for.
      * @return {@code true} if there are ongoing calls, {@code false} otherwise.
-     * @throws SecurityException if detectForAllUsers is true or userHandle is not the calling user
-     * and the caller does not grant the ability to interact across users.
+     * @throws SecurityException if the userHandle is not the calling user and the caller does not
+     * grant the ability to interact across users.
      * @hide
      */
     @SystemApi
@@ -2811,11 +2808,45 @@
     @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public boolean isInSelfManagedCall(@NonNull String packageName,
-            @NonNull UserHandle userHandle, boolean detectForAllUsers) {
+            @NonNull UserHandle userHandle) {
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
                 return service.isInSelfManagedCall(packageName, userHandle,
+                        mContext.getOpPackageName(), false);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
+                e.rethrowFromSystemServer();
+                return false;
+            }
+        } else {
+            throw new IllegalStateException("Telecom service is not present");
+        }
+    }
+
+    /**
+     * Determines whether there are any ongoing {@link PhoneAccount#CAPABILITY_SELF_MANAGED}
+     * calls for a given {@code packageName} amongst all users, given that detectForAllUsers is true
+     * and the caller has the ability to interact across users. If detectForAllUsers isn't enabled,
+     * the calls will be checked against the caller.
+     *
+     * @param packageName the package name of the app to check calls for.
+     * @param detectForAllUsers indicates if calls should be detected across all users.
+     * @return {@code true} if there are ongoing calls, {@code false} otherwise.
+     * @throws SecurityException if detectForAllUsers is true and the caller does not grant the
+     * ability to interact across users.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isInSelfManagedCall(@NonNull String packageName,
+            boolean detectForAllUsers) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                return service.isInSelfManagedCall(packageName, null,
                         mContext.getOpPackageName(), detectForAllUsers);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 5af2c34..5524541 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -856,10 +856,22 @@
                 int slotId, IGetAvailableMemoryInBytesCallback callback) {
             mExecutor.execute(
                     () -> {
-                        long availableMemoryInBytes =
-                                EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+                        long availableMemoryInBytes = EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE;
+                        String unsupportedOperationMessage = "";
                         try {
-                            callback.onSuccess(availableMemoryInBytes);
+                            availableMemoryInBytes =
+                                    EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+                        } catch (UnsupportedOperationException e) {
+                            unsupportedOperationMessage = e.getMessage();
+                        }
+
+                        try {
+                            if (!unsupportedOperationMessage.isEmpty()) {
+                                callback.onUnsupportedOperationException(
+                                        unsupportedOperationMessage);
+                            } else {
+                                callback.onSuccess(availableMemoryInBytes);
+                            }
                         } catch (RemoteException e) {
                             // Can't communicate with the phone process; ignore.
                         }
diff --git a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
index bd6d19b..e550e77 100644
--- a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
+++ b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
@@ -19,4 +19,5 @@
 /** @hide */
 oneway interface IGetAvailableMemoryInBytesCallback {
     void onSuccess(long availableMemoryInBytes);
+    void onUnsupportedOperationException(String message);
 }