Merge "Add merged local insets to proto" into udc-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index f48e078..3b5f11b 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -265,7 +265,8 @@
      * @see JobInfo.Builder#setRequiredNetworkType(int)
      */
     public void onNetworkChanged(@NonNull JobParameters params) {
-        Log.w(TAG, "onNetworkChanged() not implemented. Must override in a subclass.");
+        Log.w(TAG, "onNetworkChanged() not implemented in " + getClass().getName()
+                + ". Must override in a subclass.");
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index c707069..2550a27 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -1351,6 +1351,7 @@
         }
 
         @Override
+        @EconomyManager.EnabledMode
         public int getEnabledMode() {
             return InternalResourceService.this.getEnabledMode();
         }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2d9a99c..9bbc4a6 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -538,6 +538,7 @@
 
   public class DevicePolicyManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void calculateHasIncompatibleAccounts();
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
     method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
     method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 95e446d..021f932 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -45,6 +45,7 @@
 import android.os.WorkSource;
 import android.util.ArraySet;
 import android.util.Pair;
+import android.util.StatsEvent;
 
 import com.android.internal.os.TimeoutRecord;
 
@@ -1217,4 +1218,10 @@
      */
     public abstract void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
             @MediaProjectionTokenEvent int event);
+
+    /**
+     * @return The stats event for the cached apps high watermark since last pull.
+     */
+    @NonNull
+    public abstract StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull);
 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index ebd525e..6592019 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -303,7 +303,7 @@
     private final Context mContext;
     private final boolean mWcgEnabled;
     private final ColorManagementProxy mCmProxy;
-    private Boolean mIsLockscreenLiveWallpaperEnabled = null;
+    private static Boolean sIsLockscreenLiveWallpaperEnabled = null;
 
     /**
      * Special drawable that draws a wallpaper as fast as possible.  Assumes
@@ -823,18 +823,18 @@
     @TestApi
     public boolean isLockscreenLiveWallpaperEnabled() {
         if (sGlobals == null) {
-            mIsLockscreenLiveWallpaperEnabled = SystemProperties.getBoolean(
+            sIsLockscreenLiveWallpaperEnabled = SystemProperties.getBoolean(
                     "persist.wm.debug.lockscreen_live_wallpaper", false);
         }
-        if (mIsLockscreenLiveWallpaperEnabled == null) {
+        if (sIsLockscreenLiveWallpaperEnabled == null) {
             try {
-                mIsLockscreenLiveWallpaperEnabled =
+                sIsLockscreenLiveWallpaperEnabled =
                         sGlobals.mService.isLockscreenLiveWallpaperEnabled();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mIsLockscreenLiveWallpaperEnabled;
+        return sIsLockscreenLiveWallpaperEnabled;
     }
 
     /**
@@ -2757,7 +2757,7 @@
     public static InputStream openDefaultWallpaper(Context context, @SetWallpaperFlags int which) {
         final String whichProp;
         final int defaultResId;
-        if (which == FLAG_LOCK) {
+        if (which == FLAG_LOCK && !sIsLockscreenLiveWallpaperEnabled) {
             /* Factory-default lock wallpapers are not yet supported
             whichProp = PROP_LOCK_WALLPAPER;
             defaultResId = com.android.internal.R.drawable.default_lock_wallpaper;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4d3338b..a8a2ad1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16825,6 +16825,23 @@
     }
 
     /**
+     * Recalculate the incompatible accounts cache.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void calculateHasIncompatibleAccounts() {
+        if (mService != null) {
+            try {
+                mService.calculateHasIncompatibleAccounts();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * @return {@code true} if bypassing the device policy management role qualification is allowed
      * with the current state of the device.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9b0b18a..9795cab 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -608,4 +608,6 @@
 
     boolean isDeviceFinanced(String callerPackageName);
     String getFinancedDeviceKioskRoleHolder(String callerPackageName);
+
+    void calculateHasIncompatibleAccounts();
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 154068e..74a69a6 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
 import static android.content.ContentProvider.maybeAddUserId;
 
 import android.Manifest;
@@ -12463,4 +12464,19 @@
     public boolean isDocument() {
         return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
     }
+
+    /** @hide */
+    public boolean isSandboxActivity(@NonNull Context context) {
+        if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
+            return true;
+        }
+        final String sandboxPackageName = context.getPackageManager().getSdkSandboxPackageName();
+        if (mPackage != null && mPackage.equals(sandboxPackageName)) {
+            return true;
+        }
+        if (mComponent != null && mComponent.getPackageName().equals(sandboxPackageName)) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index 6b09c30..5eb6526 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -34,6 +34,8 @@
     @VisibleForTesting
     static final SparseArray<FontScaleConverter> LOOKUP_TABLES = new SparseArray<>();
 
+    private static float sMinScaleBeforeCurvesApplied = 1.05f;
+
     static {
         // These were generated by frameworks/base/tools/fonts/font-scaling-array-generator.js and
         // manually tweaked for optimum readability.
@@ -82,11 +84,30 @@
                         new float[] {  16f,   20f,   24f,   26f,   30f,   34f,   36f,   38f,  100})
         );
 
+        sMinScaleBeforeCurvesApplied = getScaleFromKey(LOOKUP_TABLES.keyAt(0)) - 0.02f;
+        if (sMinScaleBeforeCurvesApplied <= 1.0f) {
+            throw new IllegalStateException(
+                    "You should only apply non-linear scaling to font scales > 1"
+            );
+        }
     }
 
     private FontScaleConverterFactory() {}
 
     /**
+     * Returns true if non-linear font scaling curves would be in effect for the given scale, false
+     * if the scaling would follow a linear curve or for no scaling.
+     *
+     * <p>Example usage:
+     * <code>isNonLinearFontScalingActive(getResources().getConfiguration().fontScale)</code>
+     *
+     * @hide
+     */
+    public static boolean isNonLinearFontScalingActive(float fontScale) {
+        return fontScale >= sMinScaleBeforeCurvesApplied;
+    }
+
+    /**
      * Finds a matching FontScaleConverter for the given fontScale factor.
      *
      * @param fontScale the scale factor, usually from {@link Configuration#fontScale}.
@@ -97,10 +118,7 @@
      */
     @Nullable
     public static FontScaleConverter forScale(float fontScale) {
-        if (fontScale <= 1) {
-            // We don't need non-linear curves for shrinking text or for 100%.
-            // Also, fontScale==0 should not have a curve either.
-            // And ignore negative font scales; that's just silly.
+        if (!isNonLinearFontScalingActive(fontScale)) {
             return null;
         }
 
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 72a3f6c..b453304 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -53,12 +53,16 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 
+import libcore.util.EmptyArray;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.function.Predicate;
 
 
 /**
@@ -86,9 +90,6 @@
     @GuardedBy("mLock")
     private final WeakDisplayCache mDisplayCache = new WeakDisplayCache();
 
-    @GuardedBy("mLock")
-    private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
-
     /**
      * Broadcast receiver that indicates when the Wifi display status changes.
      * <p>
@@ -627,9 +628,7 @@
      * @return The display object, or null if there is no valid display with the given id.
      */
     public Display getDisplay(int displayId) {
-        synchronized (mLock) {
-            return getOrCreateDisplayLocked(displayId, false /*assumeValid*/);
-        }
+        return getOrCreateDisplay(displayId, false /*assumeValid*/);
     }
 
     /**
@@ -661,75 +660,67 @@
         boolean includeDisabled = (category != null
                 && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
         final int[] displayIds = mGlobal.getDisplayIds(includeDisabled);
-        synchronized (mLock) {
-            try {
-                if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
-                    addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI,
-                            Display.FLAG_PRESENTATION);
-                    addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL,
-                            Display.FLAG_PRESENTATION);
-                    addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY,
-                            Display.FLAG_PRESENTATION);
-                    addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL,
-                            Display.FLAG_PRESENTATION);
-                    addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL,
-                            Display.FLAG_PRESENTATION);
-                } else if (DISPLAY_CATEGORY_REAR.equals(category)) {
-                    addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL,
-                            Display.FLAG_REAR);
-                } else if (category == null
-                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
-                    addAllDisplaysLocked(mTempDisplays, displayIds);
-                }
-                return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
-            } finally {
-                mTempDisplays.clear();
-            }
+        if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
+            return getDisplays(displayIds, DisplayManager::isPresentationDisplay);
+        } else if (DISPLAY_CATEGORY_REAR.equals(category)) {
+            return getDisplays(displayIds, DisplayManager::isRearDisplay);
+        } else if (category == null || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+            return getDisplays(displayIds, Objects::nonNull);
         }
+        return (Display[]) EmptyArray.OBJECT;
     }
 
-    @GuardedBy("mLock")
-    private void addAllDisplaysLocked(ArrayList<Display> displays, int[] displayIds) {
-        for (int i = 0; i < displayIds.length; i++) {
-            Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
-            if (display != null) {
-                displays.add(display);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void addDisplaysLocked(
-            ArrayList<Display> displays, int[] displayIds, int matchType, int flagMask) {
+    private Display[] getDisplays(int[] displayIds, Predicate<Display> predicate) {
+        ArrayList<Display> tmpDisplays = new ArrayList<>();
         for (int displayId : displayIds) {
-            if (displayId == DEFAULT_DISPLAY) {
-                continue;
+            Display display = getOrCreateDisplay(displayId, /*assumeValid=*/true);
+            if (predicate.test(display)) {
+                tmpDisplays.add(display);
             }
+        }
+        return tmpDisplays.toArray(new Display[tmpDisplays.size()]);
+    }
 
-            Display display = getOrCreateDisplayLocked(displayId, /* assumeValid= */ true);
-            if (display != null
-                    && (display.getFlags() & flagMask) == flagMask
-                    && display.getType() == matchType) {
-                displays.add(display);
-            }
+    private static boolean isPresentationDisplay(@Nullable Display display) {
+        if (display == null || (display.getDisplayId() == DEFAULT_DISPLAY)
+                || (display.getFlags() & Display.FLAG_PRESENTATION) == 0) {
+            return false;
+        }
+        switch (display.getType()) {
+            case Display.TYPE_INTERNAL:
+            case Display.TYPE_EXTERNAL:
+            case Display.TYPE_WIFI:
+            case Display.TYPE_OVERLAY:
+            case Display.TYPE_VIRTUAL:
+                return true;
+            default:
+                return false;
         }
     }
 
-    @GuardedBy("mLock")
-    private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
-        Display display = mDisplayCache.get(displayId);
-        if (display == null) {
-            // TODO: We cannot currently provide any override configurations for metrics on displays
-            // other than the display the context is associated with.
-            final Resources resources = mContext.getDisplayId() == displayId
-                    ? mContext.getResources() : null;
+    private static boolean isRearDisplay(@Nullable Display display) {
+        return display != null && display.getDisplayId() != DEFAULT_DISPLAY
+                && display.getType() == Display.TYPE_INTERNAL
+                && (display.getFlags() & Display.FLAG_REAR) != 0;
+    }
 
-            display = mGlobal.getCompatibleDisplay(displayId, resources);
-            if (display != null) {
-                mDisplayCache.put(display);
+    private Display getOrCreateDisplay(int displayId, boolean assumeValid) {
+        Display display;
+        synchronized (mLock) {
+            display = mDisplayCache.get(displayId);
+            if (display == null) {
+                // TODO: We cannot currently provide any override configurations for metrics on
+                // displays other than the display the context is associated with.
+                final Resources resources = mContext.getDisplayId() == displayId
+                        ? mContext.getResources() : null;
+
+                display = mGlobal.getCompatibleDisplay(displayId, resources);
+                if (display != null) {
+                    mDisplayCache.put(display);
+                }
+            } else if (!assumeValid && !display.isValid()) {
+                display = null;
             }
-        } else if (!assumeValid && !display.isValid()) {
-            display = null;
         }
         return display;
     }
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 55b20e1..e0af913 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -342,6 +342,14 @@
             return;
         }
 
+        if (getEnrolledFaces(userId).size()
+                >= mContext.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)) {
+            callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+                    getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */));
+            return;
+        }
+
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2fec02f..a0cceae 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -39,7 +39,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Vibrator;
-import android.sysprop.InputProperties;
 import android.util.Log;
 import android.view.Display;
 import android.view.InputDevice;
@@ -1038,9 +1037,7 @@
      */
     public boolean isStylusPointerIconEnabled() {
         if (mIsStylusPointerIconEnabled == null) {
-            mIsStylusPointerIconEnabled = mContext.getResources()
-                    .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
-                    || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
+            mIsStylusPointerIconEnabled = InputSettings.isStylusPointerIconEnabled(mContext);
         }
         return mIsStylusPointerIconEnabled;
     }
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 5462171..c0877d3 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -1252,7 +1252,7 @@
     /**
      * @see InputManager#requestPointerCapture(IBinder, boolean)
      */
-    void requestPointerCapture(IBinder windowToken, boolean enable) {
+    public void requestPointerCapture(IBinder windowToken, boolean enable) {
         try {
             mIm.requestPointerCapture(windowToken, enable);
         } catch (RemoteException ex) {
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cdf9ea5..6cd32ff 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.sysprop.InputProperties;
 
 /**
  * InputSettings encapsulates reading and writing settings related to input
@@ -316,4 +317,15 @@
                 Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0,
                 UserHandle.USER_CURRENT);
     }
+
+    /**
+     * Whether a pointer icon will be shown over the location of a
+     * stylus pointer.
+     * @hide
+     */
+    public static boolean isStylusPointerIconEnabled(@NonNull Context context) {
+        return context.getResources()
+                       .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
+               || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d75d186..561f798 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14906,6 +14906,14 @@
         public static final String ENABLE_TARE = "enable_tare";
 
         /**
+         * Whether to show the TARE page in Developer Options or not.
+         * 1 = true, everything else = false
+         *
+         * @hide
+         */
+        public static final String SHOW_TARE_DEVELOPER_OPTIONS = "show_tare_developer_options";
+
+        /**
          * Settings for AlarmManager's TARE EconomicPolicy (list of its economic factors).
          *
          * Keys are listed in {@link android.app.tare.EconomyManager}.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 77bbeb5..9d3d70d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -112,6 +112,7 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Supplier;
 
 /**
@@ -431,6 +432,7 @@
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0,
                         mergedConfiguration);
+                mIWallpaperEngine.mPendingResizeCount.incrementAndGet();
                 mCaller.sendMessage(msg);
             }
 
@@ -1051,6 +1053,10 @@
             out.print(prefix); out.print("mZoom="); out.println(mZoom);
             out.print(prefix); out.print("mPreviewSurfacePosition=");
                     out.println(mPreviewSurfacePosition);
+            final int pendingCount = mIWallpaperEngine.mPendingResizeCount.get();
+            if (pendingCount != 0) {
+                out.print(prefix); out.print("mPendingResizeCount="); out.println(pendingCount);
+            }
             synchronized (mLock) {
                 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
                         out.print(" mPendingXOffset="); out.println(mPendingXOffset);
@@ -1113,10 +1119,6 @@
             }
         }
 
-        private void updateConfiguration(MergedConfiguration mergedConfiguration) {
-            mMergedConfiguration.setTo(mergedConfiguration);
-        }
-
         void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
             if (mDestroyed) {
                 Log.w(TAG, "Ignoring updateSurface due to destroyed");
@@ -1165,7 +1167,7 @@
                             | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 
                     final Configuration config = mMergedConfiguration.getMergedConfiguration();
-                    final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+                    final Rect maxBounds = new Rect(config.windowConfiguration.getMaxBounds());
                     if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
                             && myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
                         mLayout.width = myWidth;
@@ -1221,6 +1223,17 @@
                     final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
                             mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+                    final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration()
+                            .windowConfiguration.getMaxBounds();
+                    if (!outMaxBounds.equals(maxBounds)) {
+                        Log.i(TAG, "Retry updateSurface because bounds changed from relayout: "
+                                + maxBounds + " -> " + outMaxBounds);
+                        mSurfaceHolder.mSurfaceLock.unlock();
+                        mDrawingAllowed = false;
+                        mCaller.sendMessage(mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
+                                redrawNeeded ? 1 : 0));
+                        return;
+                    }
 
                     final int transformHint = SurfaceControl.rotationToBufferTransform(
                             (mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4);
@@ -1488,6 +1501,8 @@
             mWallpaperDimAmount = mDefaultDimAmount;
             mPreviousWallpaperDimAmount = mWallpaperDimAmount;
             mDisplayState = mDisplay.getCommittedState();
+            mMergedConfiguration.setOverrideConfiguration(
+                    mDisplayContext.getResources().getConfiguration());
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
             Trace.beginSection("WPMS.Engine.onCreate");
@@ -2324,6 +2339,8 @@
         final IBinder mWindowToken;
         final int mWindowType;
         final boolean mIsPreview;
+        final AtomicInteger mPendingResizeCount = new AtomicInteger();
+        boolean mReportDraw;
         boolean mShownReported;
         int mReqWidth;
         int mReqHeight;
@@ -2579,11 +2596,7 @@
                     mEngine.doCommand(cmd);
                 } break;
                 case MSG_WINDOW_RESIZED: {
-                    final boolean reportDraw = message.arg1 != 0;
-                    mEngine.updateConfiguration(((MergedConfiguration) message.obj));
-                    mEngine.updateSurface(true, false, reportDraw);
-                    mEngine.doOffsetsChanged(true);
-                    mEngine.scaleAndCropScreenshot();
+                    handleResized((MergedConfiguration) message.obj, message.arg1 != 0);
                 } break;
                 case MSG_WINDOW_MOVED: {
                     // Do nothing. What does it mean for a Wallpaper to move?
@@ -2631,6 +2644,40 @@
                     Log.w(TAG, "Unknown message type " + message.what);
             }
         }
+
+        /**
+         * In general this performs relayout for IWindow#resized. If there are several pending
+         * (in the message queue) MSG_WINDOW_RESIZED from server side, only the last one will be
+         * handled (ignore intermediate states). Note that this procedure cannot be skipped if the
+         * configuration is not changed because this is also used to dispatch insets changes.
+         */
+        private void handleResized(MergedConfiguration config, boolean reportDraw) {
+            // The config can be null when retrying for a changed config from relayout, otherwise
+            // it is from IWindow#resized which always sends non-null config.
+            final int pendingCount = config != null ? mPendingResizeCount.decrementAndGet() : -1;
+            if (reportDraw) {
+                mReportDraw = true;
+            }
+            if (pendingCount > 0) {
+                if (DEBUG) {
+                    Log.d(TAG, "Skip outdated resize, bounds="
+                            + config.getMergedConfiguration().windowConfiguration.getMaxBounds()
+                            + " pendingCount=" + pendingCount);
+                }
+                return;
+            }
+            if (config != null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Update config from resized, bounds="
+                            + config.getMergedConfiguration().windowConfiguration.getMaxBounds());
+                }
+                mEngine.mMergedConfiguration.setTo(config);
+            }
+            mEngine.updateSurface(true /* forceRelayout */, false /* forceReport */, mReportDraw);
+            mReportDraw = false;
+            mEngine.doOffsetsChanged(true);
+            mEngine.scaleAndCropScreenshot();
+        }
     }
 
     /**
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index b93e338..330a9fc 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -385,10 +385,22 @@
      *
      * @return The complex unit type.
      */
-     public int getComplexUnit()
-     {
-         return COMPLEX_UNIT_MASK & (data>>TypedValue.COMPLEX_UNIT_SHIFT);
-     }
+    public int getComplexUnit() {
+        return getUnitFromComplexDimension(data);
+    }
+
+    /**
+     * Return the complex unit type for the given complex dimension. For example, a dimen type
+     * with value 12sp will return {@link #COMPLEX_UNIT_SP}. Use with values created with {@link
+     * #createComplexDimension(int, int)} etc.
+     *
+     * @return The complex unit type.
+     *
+     * @hide
+     */
+    public static int getUnitFromComplexDimension(int complexDimension) {
+        return COMPLEX_UNIT_MASK & (complexDimension >> TypedValue.COMPLEX_UNIT_SHIFT);
+    }
 
     /**
      * Converts an unpacked complex data value holding a dimension to its final floating point pixel
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 8801fe0..4996f5a 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -43,7 +43,8 @@
     private final InputChannel mInputChannel;
     @NonNull
     private final IInputMonitorHost mHost;
-
+    @NonNull
+    private final SurfaceControl mSurface;
 
     /**
      * Takes all of the current pointer events streams that are currently being sent to this
@@ -70,6 +71,7 @@
      */
     public void dispose() {
         mInputChannel.dispose();
+        mSurface.release();
         try {
             mHost.dispose();
         } catch (RemoteException e) {
@@ -95,13 +97,17 @@
     @DataClass.Generated.Member
     public InputMonitor(
             @NonNull InputChannel inputChannel,
-            @NonNull IInputMonitorHost host) {
+            @NonNull IInputMonitorHost host,
+            @NonNull SurfaceControl surface) {
         this.mInputChannel = inputChannel;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mInputChannel);
         this.mHost = host;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mHost);
+        this.mSurface = surface;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSurface);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -116,6 +122,11 @@
         return mHost;
     }
 
+    @DataClass.Generated.Member
+    public @NonNull SurfaceControl getSurface() {
+        return mSurface;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -124,7 +135,8 @@
 
         return "InputMonitor { " +
                 "inputChannel = " + mInputChannel + ", " +
-                "host = " + mHost +
+                "host = " + mHost + ", " +
+                "surface = " + mSurface +
         " }";
     }
 
@@ -136,6 +148,7 @@
 
         dest.writeTypedObject(mInputChannel, flags);
         dest.writeStrongInterface(mHost);
+        dest.writeTypedObject(mSurface, flags);
     }
 
     @Override
@@ -151,6 +164,7 @@
 
         InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
         IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+        SurfaceControl surface = (SurfaceControl) in.readTypedObject(SurfaceControl.CREATOR);
 
         this.mInputChannel = inputChannel;
         com.android.internal.util.AnnotationValidations.validate(
@@ -158,6 +172,9 @@
         this.mHost = host;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mHost);
+        this.mSurface = surface;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSurface);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -177,10 +194,10 @@
     };
 
     @DataClass.Generated(
-            time = 1637697281750L,
+            time = 1679692514588L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
-            inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
+            inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\nprivate final @android.annotation.NonNull android.view.SurfaceControl mSurface\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 75f1666..6c5f195 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -480,9 +480,9 @@
                     : inset != 0;
 
             if (outState != null && source != null) {
-                outState.getOrCreateSource(source.getId(), source.getType())
+                outState.addSource(new InsetsSource(source)
                         .setVisible(visible)
-                        .setFrame(mTmpFrame);
+                        .setFrame(mTmpFrame));
             }
 
             // If the system is controlling the insets source, the leash can be null.
diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java
index 2d7dc31..a69af24 100644
--- a/core/java/android/view/InsetsFrameProvider.java
+++ b/core/java/android/view/InsetsFrameProvider.java
@@ -23,6 +23,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.InsetsSource.Flags;
 import android.view.WindowInsets.Type.InsetsType;
 
 import java.util.Arrays;
@@ -86,6 +87,13 @@
     private Insets mInsetsSize = null;
 
     /**
+     * Various behavioral options/flags. Default is none.
+     *
+     * @see Flags
+     */
+    private @Flags int mFlags;
+
+    /**
      * If null, the size set in insetsSize will be applied to all window types. If it contains
      * element of some types, the insets reported to the window with that types will be overridden.
      */
@@ -149,6 +157,15 @@
         return mSource;
     }
 
+    public InsetsFrameProvider setFlags(@Flags int flags, @Flags int mask) {
+        mFlags = (mFlags & ~mask) | (flags & mask);
+        return this;
+    }
+
+    public @Flags int getFlags() {
+        return mFlags;
+    }
+
     public InsetsFrameProvider setInsetsSize(Insets insetsSize) {
         mInsetsSize = insetsSize;
         return this;
@@ -198,6 +215,7 @@
         sb.append(", index=").append(mIndex);
         sb.append(", type=").append(WindowInsets.Type.toString(mType));
         sb.append(", source=").append(sourceToString(mSource));
+        sb.append(", flags=[").append(InsetsSource.flagsToString(mFlags)).append("]");
         if (mInsetsSize != null) {
             sb.append(", insetsSize=").append(mInsetsSize);
         }
@@ -230,6 +248,7 @@
         mIndex = in.readInt();
         mType = in.readInt();
         mSource = in.readInt();
+        mFlags = in.readInt();
         mInsetsSize = in.readTypedObject(Insets.CREATOR);
         mInsetsSizeOverrides = in.createTypedArray(InsetsSizeOverride.CREATOR);
         mArbitraryRectangle = in.readTypedObject(Rect.CREATOR);
@@ -241,6 +260,7 @@
         out.writeInt(mIndex);
         out.writeInt(mType);
         out.writeInt(mSource);
+        out.writeInt(mFlags);
         out.writeTypedObject(mInsetsSize, flags);
         out.writeTypedArray(mInsetsSizeOverrides, flags);
         out.writeTypedObject(mArbitraryRectangle, flags);
@@ -260,7 +280,7 @@
         }
         final InsetsFrameProvider other = (InsetsFrameProvider) o;
         return Objects.equals(mOwner, other.mOwner) && mIndex == other.mIndex
-                && mType == other.mType && mSource == other.mSource
+                && mType == other.mType && mSource == other.mSource && mFlags == other.mFlags
                 && Objects.equals(mInsetsSize, other.mInsetsSize)
                 && Arrays.equals(mInsetsSizeOverrides, other.mInsetsSizeOverrides)
                 && Objects.equals(mArbitraryRectangle, other.mArbitraryRectangle);
@@ -268,7 +288,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mOwner, mIndex, mType, mSource, mInsetsSize,
+        return Objects.hash(mOwner, mIndex, mType, mSource, mFlags, mInsetsSize,
                 Arrays.hashCode(mInsetsSizeOverrides), mArbitraryRectangle);
     }
 
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 3947738..bd48771 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -22,6 +22,7 @@
 import static android.view.InsetsSourceProto.VISIBLE_FRAME;
 import static android.view.WindowInsets.Type.ime;
 
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,7 +34,10 @@
 import android.view.WindowInsets.Type.InsetsType;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
+import java.util.StringJoiner;
 
 /**
  * Represents the state of a single entity generating insets for clients.
@@ -45,6 +49,24 @@
     public static final int ID_IME = createId(null, 0, ime());
 
     /**
+     * Controls whether this source suppresses the scrim. If the scrim is ignored, the system won't
+     * draw a semi-transparent scrim behind the system bar area even when the bar contrast is
+     * enforced.
+     *
+     * @see android.R.styleable#Window_enforceStatusBarContrast
+     * @see android.R.styleable#Window_enforceNavigationBarContrast
+     */
+    public static final int FLAG_SUPPRESS_SCRIM = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = "FLAG_", value = {
+            FLAG_SUPPRESS_SCRIM,
+    })
+    public @interface Flags {}
+
+    private @Flags int mFlags;
+
+    /**
      * An unique integer to identify this source across processes.
      */
     private final int mId;
@@ -75,6 +97,7 @@
         mVisibleFrame = other.mVisibleFrame != null
                 ? new Rect(other.mVisibleFrame)
                 : null;
+        mFlags = other.mFlags;
         mInsetsRoundedCornerFrame = other.mInsetsRoundedCornerFrame;
     }
 
@@ -84,6 +107,7 @@
         mVisibleFrame = other.mVisibleFrame != null
                 ? new Rect(other.mVisibleFrame)
                 : null;
+        mFlags = other.mFlags;
         mInsetsRoundedCornerFrame = other.mInsetsRoundedCornerFrame;
     }
 
@@ -107,6 +131,11 @@
         return this;
     }
 
+    public InsetsSource setFlags(@Flags int flags) {
+        mFlags = flags;
+        return this;
+    }
+
     public int getId() {
         return mId;
     }
@@ -127,6 +156,10 @@
         return mVisible;
     }
 
+    public @Flags int getFlags() {
+        return mFlags;
+    }
+
     boolean isUserControllable() {
         // If mVisibleFrame is null, it will be the same area as mFrame.
         return mVisibleFrame == null || !mVisibleFrame.isEmpty();
@@ -254,6 +287,14 @@
                 + WindowInsets.Type.indexOf(type);
     }
 
+    public static String flagsToString(@Flags int flags) {
+        final StringJoiner joiner = new StringJoiner(" ");
+        if ((flags & FLAG_SUPPRESS_SCRIM) != 0) {
+            joiner.add("SUPPRESS_SCRIM");
+        }
+        return joiner.toString();
+    }
+
     /**
      * Export the state of {@link InsetsSource} into a protocol buffer output stream.
      *
@@ -280,6 +321,7 @@
             pw.print(" visibleFrame="); pw.print(mVisibleFrame.toShortString());
         }
         pw.print(" visible="); pw.print(mVisible);
+        pw.print(" flags="); pw.print(flagsToString(mFlags));
         pw.print(" insetsRoundedCornerFrame="); pw.print(mInsetsRoundedCornerFrame);
         pw.println();
     }
@@ -302,6 +344,7 @@
         if (mId != that.mId) return false;
         if (mType != that.mType) return false;
         if (mVisible != that.mVisible) return false;
+        if (mFlags != that.mFlags) return false;
         if (excludeInvisibleImeFrames && !mVisible && mType == WindowInsets.Type.ime()) return true;
         if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
         if (mInsetsRoundedCornerFrame != that.mInsetsRoundedCornerFrame) return false;
@@ -310,7 +353,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mType, mFrame, mVisibleFrame, mVisible, mInsetsRoundedCornerFrame);
+        return Objects.hash(mId, mType, mFrame, mVisibleFrame, mVisible, mFlags,
+                mInsetsRoundedCornerFrame);
     }
 
     public InsetsSource(Parcel in) {
@@ -323,6 +367,7 @@
             mVisibleFrame = null;
         }
         mVisible = in.readBoolean();
+        mFlags = in.readInt();
         mInsetsRoundedCornerFrame = in.readBoolean();
     }
 
@@ -343,6 +388,7 @@
             dest.writeInt(0);
         }
         dest.writeBoolean(mVisible);
+        dest.writeInt(mFlags);
         dest.writeBoolean(mInsetsRoundedCornerFrame);
     }
 
@@ -352,6 +398,7 @@
                 + " mType=" + WindowInsets.Type.toString(mType)
                 + " mFrame=" + mFrame.toShortString()
                 + " mVisible=" + mVisible
+                + " mFlags=[" + flagsToString(mFlags) + "]"
                 + (mInsetsRoundedCornerFrame ? " insetsRoundedCornerFrame" : "")
                 + "}";
     }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bd249c4..5b974cd 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -143,9 +143,14 @@
         boolean[] typeVisibilityMap = new boolean[Type.SIZE];
         final Rect relativeFrame = new Rect(frame);
         final Rect relativeFrameMax = new Rect(frame);
+        @InsetsType int suppressScrimTypes = 0;
         for (int i = mSources.size() - 1; i >= 0; i--) {
             final InsetsSource source = mSources.valueAt(i);
 
+            if ((source.getFlags() & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) {
+                suppressScrimTypes |= source.getType();
+            }
+
             processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
                     idSideMap, typeVisibilityMap);
 
@@ -177,7 +182,7 @@
         }
 
         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
-                alwaysConsumeSystemBars, calculateRelativeCutout(frame),
+                alwaysConsumeSystemBars, suppressScrimTypes, calculateRelativeCutout(frame),
                 calculateRelativeRoundedCorners(frame),
                 calculateRelativePrivacyIndicatorBounds(frame),
                 calculateRelativeDisplayShape(frame),
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index bd6224b..d987217 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -410,6 +410,13 @@
     }
 
     /**
+     * @hide
+     */
+    public @NonNull AttachedSurfaceControl getRootSurfaceControl() {
+        return mViewRoot;
+    }
+
+    /**
      * Set the root view of the SurfaceControlViewHost. This view will render in to
      * the SurfaceControl, and receive input based on the SurfaceControls positioning on
      * screen. It will be laid as if it were in a window of the passed in width and height.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 86e7fb0..2b29e78 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -133,7 +133,9 @@
 import android.graphics.drawable.GradientDrawable;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.input.InputManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.input.InputManagerGlobal;
+import android.hardware.input.InputSettings;
 import android.media.AudioManager;
 import android.os.Binder;
 import android.os.Build;
@@ -443,9 +445,7 @@
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
     @NonNull Display mDisplay;
-    final DisplayManager mDisplayManager;
     final String mBasePackageName;
-    final InputManager mInputManager;
 
     final int[] mTmpLocation = new int[2];
 
@@ -550,6 +550,9 @@
     // Whether to draw this surface as DISPLAY_DECORATION.
     boolean mDisplayDecorationCached = false;
 
+    // Is the stylus pointer icon enabled
+    private final boolean mIsStylusPointerIconEnabled;
+
     /**
      * Update the Choreographer's FrameInfo object with the timing information for the current
      * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -994,14 +997,14 @@
         mFallbackEventHandler = new PhoneFallbackEventHandler(context);
         // TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
         mChoreographer = Choreographer.getInstance();
-        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
-        mInputManager = context.getSystemService(InputManager.class);
         mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
         mHandwritingInitiator = new HandwritingInitiator(
                 mViewConfiguration,
                 mContext.getSystemService(InputMethodManager.class));
 
         mViewBoundsSandboxingEnabled = getViewBoundsSandboxingEnabled();
+        mIsStylusPointerIconEnabled =
+                InputSettings.isStylusPointerIconEnabled(mContext);
 
         String processorOverrideName = context.getResources().getString(
                                     R.string.config_inputEventCompatProcessorOverrideClassName);
@@ -1488,7 +1491,14 @@
                 mAccessibilityInteractionConnectionManager, mHandler);
         mAccessibilityManager.addHighTextContrastStateChangeListener(
                 mHighContrastTextManager, mHandler);
-        mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+        DisplayManagerGlobal
+                .getInstance()
+                .registerDisplayListener(
+                        mDisplayListener,
+                        mHandler,
+                        DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
     }
 
     /**
@@ -1499,7 +1509,9 @@
                 mAccessibilityInteractionConnectionManager);
         mAccessibilityManager.removeHighTextContrastStateChangeListener(
                 mHighContrastTextManager);
-        mDisplayManager.unregisterDisplayListener(mDisplayListener);
+        DisplayManagerGlobal
+                .getInstance()
+                .unregisterDisplayListener(mDisplayListener);
     }
 
     private void setTag() {
@@ -5382,7 +5394,9 @@
             Log.e(mTag, "No input channel to request Pointer Capture.");
             return;
         }
-        mInputManager.requestPointerCapture(inputToken, enabled);
+        InputManagerGlobal
+                .getInstance()
+                .requestPointerCapture(inputToken, enabled);
     }
 
     private void handlePointerCaptureChanged(boolean hasCapture) {
@@ -6947,7 +6961,7 @@
             }
             final boolean needsStylusPointerIcon = event.isStylusPointer()
                     && event.isHoverEvent()
-                    && mInputManager.isStylusPointerIconEnabled();
+                    && mIsStylusPointerIconEnabled;
             if (needsStylusPointerIcon || event.isFromSource(InputDevice.SOURCE_MOUSE)) {
                 if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
                         || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
@@ -7018,8 +7032,7 @@
         }
 
         PointerIcon pointerIcon = null;
-
-        if (event.isStylusPointer() && mInputManager.isStylusPointerIconEnabled()) {
+        if (event.isStylusPointer() && mIsStylusPointerIconEnabled) {
             pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event);
         }
 
@@ -7034,14 +7047,18 @@
             mPointerIconType = pointerType;
             mCustomPointerIcon = null;
             if (mPointerIconType != PointerIcon.TYPE_CUSTOM) {
-                mInputManager.setPointerIconType(pointerType);
+                InputManagerGlobal
+                    .getInstance()
+                    .setPointerIconType(pointerType);
                 return true;
             }
         }
         if (mPointerIconType == PointerIcon.TYPE_CUSTOM &&
                 !pointerIcon.equals(mCustomPointerIcon)) {
             mCustomPointerIcon = pointerIcon;
-            mInputManager.setCustomPointerIcon(mCustomPointerIcon);
+            InputManagerGlobal
+                    .getInstance()
+                    .setCustomPointerIcon(mCustomPointerIcon);
         }
         return true;
     }
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index fd55d8d..4acaea8 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -91,6 +91,7 @@
      */
     private final boolean mAlwaysConsumeSystemBars;
 
+    private final @InsetsType int mSuppressScrimTypes;
     private final boolean mSystemWindowInsetsConsumed;
     private final boolean mStableInsetsConsumed;
     private final boolean mDisplayCutoutConsumed;
@@ -116,8 +117,8 @@
 
     static {
         CONSUMED = new WindowInsets(createCompatTypeMap(null), createCompatTypeMap(null),
-                createCompatVisibilityMap(createCompatTypeMap(null)), false, false, null, null,
-                null, null, systemBars(), false);
+                createCompatVisibilityMap(createCompatTypeMap(null)), false, false, 0, null,
+                null, null, null, systemBars(), false);
     }
 
     /**
@@ -136,7 +137,8 @@
             @Nullable Insets[] typeMaxInsetsMap,
             boolean[] typeVisibilityMap,
             boolean isRound,
-            boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
+            boolean alwaysConsumeSystemBars, @InsetsType int suppressScrimTypes,
+            DisplayCutout displayCutout,
             RoundedCorners roundedCorners,
             PrivacyIndicatorBounds privacyIndicatorBounds,
             DisplayShape displayShape,
@@ -154,6 +156,7 @@
         mTypeVisibilityMap = typeVisibilityMap;
         mIsRound = isRound;
         mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+        mSuppressScrimTypes = suppressScrimTypes;
         mCompatInsetsTypes = compatInsetsTypes;
         mCompatIgnoreVisibility = compatIgnoreVisibility;
 
@@ -175,7 +178,8 @@
         this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
                 src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
                 src.mTypeVisibilityMap, src.mIsRound,
-                src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
+                src.mAlwaysConsumeSystemBars, src.mSuppressScrimTypes,
+                displayCutoutCopyConstructorArgument(src),
                 src.mRoundedCorners,
                 src.mPrivacyIndicatorBounds,
                 src.mDisplayShape,
@@ -231,8 +235,8 @@
     /** @hide */
     @UnsupportedAppUsage
     public WindowInsets(Rect systemWindowInsets) {
-        this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
-                null, null, null, systemBars(), false /* compatIgnoreVisibility */);
+        this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, 0,
+                null, null, null, null, systemBars(), false /* compatIgnoreVisibility */);
     }
 
     /**
@@ -552,7 +556,7 @@
         return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap,
                 mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
                 mTypeVisibilityMap,
-                mIsRound, mAlwaysConsumeSystemBars,
+                mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
                 null /* displayCutout */, mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape,
                 mCompatInsetsTypes, mCompatIgnoreVisibility);
     }
@@ -603,7 +607,7 @@
     public WindowInsets consumeSystemWindowInsets() {
         return new WindowInsets(null, null,
                 mTypeVisibilityMap,
-                mIsRound, mAlwaysConsumeSystemBars,
+                mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
                 // If the system window insets types contain displayCutout, we should also consume
                 // it.
                 (mCompatInsetsTypes & displayCutout()) != 0
@@ -895,6 +899,13 @@
         return mAlwaysConsumeSystemBars;
     }
 
+    /**
+     * @hide
+     */
+    public @InsetsType int getSuppressScrimTypes() {
+        return mSuppressScrimTypes;
+    }
+
     @Override
     public String toString() {
         StringBuilder result = new StringBuilder("WindowInsets{\n    ");
@@ -919,7 +930,9 @@
         result.append("\n    ");
         result.append(mDisplayShape != null ? "displayShape=" + mDisplayShape : "");
         result.append("\n    ");
-        result.append("compatInsetsTypes=" + mCompatInsetsTypes);
+        result.append("suppressScrimTypes=" + Type.toString(mSuppressScrimTypes));
+        result.append("\n    ");
+        result.append("compatInsetsTypes=" + Type.toString(mCompatInsetsTypes));
         result.append("\n    ");
         result.append("compatIgnoreVisibility=" + mCompatIgnoreVisibility);
         result.append("\n    ");
@@ -1014,7 +1027,7 @@
                         ? null
                         : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
                 mTypeVisibilityMap,
-                mIsRound, mAlwaysConsumeSystemBars,
+                mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
                 mDisplayCutoutConsumed
                         ? null
                         : mDisplayCutout == null
@@ -1038,6 +1051,7 @@
 
         return mIsRound == that.mIsRound
                 && mAlwaysConsumeSystemBars == that.mAlwaysConsumeSystemBars
+                && mSuppressScrimTypes == that.mSuppressScrimTypes
                 && mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
                 && mStableInsetsConsumed == that.mStableInsetsConsumed
                 && mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
@@ -1054,8 +1068,9 @@
     public int hashCode() {
         return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
                 Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
-                mAlwaysConsumeSystemBars, mSystemWindowInsetsConsumed, mStableInsetsConsumed,
-                mDisplayCutoutConsumed, mPrivacyIndicatorBounds, mDisplayShape);
+                mAlwaysConsumeSystemBars, mSuppressScrimTypes, mSystemWindowInsetsConsumed,
+                mStableInsetsConsumed, mDisplayCutoutConsumed, mPrivacyIndicatorBounds,
+                mDisplayShape);
     }
 
 
@@ -1120,6 +1135,7 @@
 
         private boolean mIsRound;
         private boolean mAlwaysConsumeSystemBars;
+        private @InsetsType int mSuppressScrimTypes;
 
         private PrivacyIndicatorBounds mPrivacyIndicatorBounds = new PrivacyIndicatorBounds();
 
@@ -1147,6 +1163,7 @@
             mRoundedCorners = insets.mRoundedCorners;
             mIsRound = insets.mIsRound;
             mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
+            mSuppressScrimTypes = insets.mSuppressScrimTypes;
             mPrivacyIndicatorBounds = insets.mPrivacyIndicatorBounds;
             mDisplayShape = insets.mDisplayShape;
         }
@@ -1420,6 +1437,13 @@
             return this;
         }
 
+        /** @hide */
+        @NonNull
+        public Builder setSuppressScrimTypes(@InsetsType int suppressScrimTypes) {
+            mSuppressScrimTypes = suppressScrimTypes;
+            return this;
+        }
+
         /**
          * Builds a {@link WindowInsets} instance.
          *
@@ -1429,8 +1453,8 @@
         public WindowInsets build() {
             return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
                     mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
-                    mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, mRoundedCorners,
-                    mPrivacyIndicatorBounds, mDisplayShape, systemBars(),
+                    mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes, mDisplayCutout,
+                    mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, systemBars(),
                     false /* compatIgnoreVisibility */);
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 67c9f8c..3fbb505 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -62,6 +62,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.content.res.FontScaleConverterFactory;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -867,6 +868,14 @@
     @UnsupportedAppUsage
     private float mSpacingAdd = 0.0f;
 
+    /**
+     * Remembers what line height was set to originally, before we broke it down into raw pixels.
+     *
+     * <p>This is stored as a complex dimension with both value and unit packed into one field!
+     * {@see TypedValue}
+     */
+    private int mLineHeightComplexDimen;
+
     private int mBreakStrategy;
     private int mHyphenationFrequency;
     private int mJustificationMode;
@@ -1233,7 +1242,8 @@
                 defStyleAttr, defStyleRes);
         int firstBaselineToTopHeight = -1;
         int lastBaselineToBottomHeight = -1;
-        int lineHeight = -1;
+        float lineHeight = -1f;
+        int lineHeightUnit = -1;
 
         readTextAppearance(context, a, attributes, true /* styleArray */);
 
@@ -1583,7 +1593,13 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_lineHeight:
-                    lineHeight = a.getDimensionPixelSize(attr, -1);
+                    TypedValue peekValue = a.peekValue(attr);
+                    if (peekValue != null && peekValue.type == TypedValue.TYPE_DIMENSION) {
+                        lineHeightUnit = peekValue.getComplexUnit();
+                        lineHeight = TypedValue.complexToFloat(peekValue.data);
+                    } else {
+                        lineHeight = a.getDimensionPixelSize(attr, -1);
+                    }
                     break;
             }
         }
@@ -1936,7 +1952,11 @@
             setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
         }
         if (lineHeight >= 0) {
-            setLineHeight(lineHeight);
+            if (lineHeightUnit == -1) {
+                setLineHeightPx(lineHeight);
+            } else {
+                setLineHeight(lineHeightUnit, lineHeight);
+            }
         }
     }
 
@@ -4629,6 +4649,7 @@
         if (size != mTextPaint.getTextSize()) {
             mTextPaint.setTextSize(size);
 
+            maybeRecalculateLineHeight();
             if (shouldRequestLayout && mLayout != null) {
                 // Do not auto-size right after setting the text size.
                 mNeedsAutoSizeText = false;
@@ -6214,6 +6235,9 @@
         if (lineHeight != fontHeight) {
             // Set lineSpacingExtra by the difference of lineSpacing with lineHeight
             setLineSpacing(lineHeight - fontHeight, 1f);
+
+            mLineHeightComplexDimen =
+                        TypedValue.createComplexDimension(lineHeight, TypedValue.COMPLEX_UNIT_PX);
         }
     }
 
@@ -6236,8 +6260,54 @@
             @TypedValue.ComplexDimensionUnit int unit,
             @FloatRange(from = 0) float lineHeight
     ) {
-        setLineHeightPx(
-                TypedValue.applyDimension(unit, lineHeight, getDisplayMetricsOrSystem()));
+        var metrics = getDisplayMetricsOrSystem();
+        // We can avoid the recalculation if we know non-linear font scaling isn't being used
+        // (an optimization for the majority case).
+        // We also don't try to do the recalculation unless both textSize and lineHeight are in SP.
+        if (!FontScaleConverterFactory.isNonLinearFontScalingActive(
+                    getResources().getConfiguration().fontScale)
+                || unit != TypedValue.COMPLEX_UNIT_SP
+                || mTextSizeUnit != TypedValue.COMPLEX_UNIT_SP
+        ) {
+            setLineHeightPx(TypedValue.applyDimension(unit, lineHeight, metrics));
+
+            // Do this last so it overwrites what setLineHeightPx() sets it to.
+            mLineHeightComplexDimen = TypedValue.createComplexDimension(lineHeight, unit);
+            return;
+        }
+
+        // Recalculate a proportional line height when non-linear font scaling is in effect.
+        // Otherwise, a desired 2x line height at font scale 1.0 will not be 2x at font scale 2.0,
+        // due to non-linear font scaling compressing higher SP sizes. See b/273326061 for details.
+        // We know they are using SP units for both the text size and the line height
+        // at this point, so determine the ratio between them. This is the *intended* line spacing
+        // multiplier if font scale == 1.0. We can then determine what the pixel value for the line
+        // height would be if we preserved proportions.
+        var textSizePx = getTextSize();
+        var textSizeSp = TypedValue.convertPixelsToDimension(
+                TypedValue.COMPLEX_UNIT_SP,
+                textSizePx,
+                metrics
+        );
+        var ratio = lineHeight / textSizeSp;
+        setLineHeightPx(textSizePx * ratio);
+
+        // Do this last so it overwrites what setLineHeightPx() sets it to.
+        mLineHeightComplexDimen = TypedValue.createComplexDimension(lineHeight, unit);
+    }
+
+    private void maybeRecalculateLineHeight() {
+        if (mLineHeightComplexDimen == 0) {
+            return;
+        }
+        int unit = TypedValue.getUnitFromComplexDimension(mLineHeightComplexDimen);
+        if (unit != TypedValue.COMPLEX_UNIT_SP) {
+            // The lineHeight was never supplied in SP, so we didn't do any fancy recalculations
+            // in setLineHeight(). We don't need to recalculate.
+            return;
+        }
+
+        setLineHeight(unit, TypedValue.complexToFloat(mLineHeightComplexDimen));
     }
 
     /**
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
index 096d1cd..6fa6fa5 100644
--- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -28,12 +28,15 @@
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__START_FOREGROUND_SERVICE;
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__UNKNOWN_ANR_TYPE;
 
+import android.annotation.IntDef;
 import android.os.SystemClock;
 import android.os.Trace;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -44,6 +47,22 @@
  */
 public class AnrLatencyTracker implements AutoCloseable {
 
+    /** Status of the early dumped pid. */
+    @IntDef(value = {
+            EarlyDumpStatus.UNKNOWN,
+            EarlyDumpStatus.SUCCEEDED,
+            EarlyDumpStatus.FAILED_TO_CREATE_FILE,
+            EarlyDumpStatus.TIMED_OUT
+    })
+
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface EarlyDumpStatus {
+        int UNKNOWN = 1;
+        int SUCCEEDED = 2;
+        int FAILED_TO_CREATE_FILE = 3;
+        int TIMED_OUT = 4;
+    }
+
     private static final AtomicInteger sNextAnrRecordPlacedOnQueueCookieGenerator =
             new AtomicInteger();
 
@@ -77,7 +96,16 @@
 
     private int mAnrQueueSize;
     private int mAnrType;
-    private int mDumpedProcessesCount = 0;
+    private final AtomicInteger mDumpedProcessesCount = new AtomicInteger(0);
+
+    private volatile @EarlyDumpStatus int mEarlyDumpStatus =
+            EarlyDumpStatus.UNKNOWN;
+    private volatile long mTempFileDumpingStartUptime;
+    private volatile long mTempFileDumpingDuration = 0;
+    private long mCopyingFirstPidStartUptime;
+    private long mCopyingFirstPidDuration = 0;
+    private long mEarlyDumpRequestSubmissionUptime = 0;
+    private long mEarlyDumpExecutorPidCount = 0;
 
     private long mFirstPidsDumpingStartUptime;
     private long mFirstPidsDumpingDuration = 0;
@@ -88,7 +116,7 @@
 
     private boolean mIsPushed = false;
     private boolean mIsSkipped = false;
-
+    private boolean mCopyingFirstPidSucceeded = false;
 
     private final int mAnrRecordPlacedOnQueueCookie =
             sNextAnrRecordPlacedOnQueueCookieGenerator.incrementAndGet();
@@ -111,6 +139,15 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
+    /**
+     * Records the number of processes we are currently early-dumping, this number includes the
+     * current ANR's main process.
+     */
+    public void earlyDumpRequestSubmittedWithSize(int currentProcessedPidCount) {
+        mEarlyDumpRequestSubmissionUptime = getUptimeMillis();
+        mEarlyDumpExecutorPidCount = currentProcessedPidCount;
+    }
+
     /** Records the placing of the AnrHelper.AnrRecord instance on the processing queue. */
     public void anrRecordPlacingOnQueueWithSize(int queueSize) {
         mAnrRecordPlacedOnQueueUptime = getUptimeMillis();
@@ -210,48 +247,89 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    /** Records the start of pid dumping to file (subject and criticalEventSection). */
+    /** Records the start of pid dumping to file. */
     public void dumpingPidStarted(int pid) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingPid#" + pid);
     }
 
-    /** Records the end of pid dumping to file (subject and criticalEventSection). */
+    /** Records the end of pid dumping to file. */
     public void dumpingPidEnded() {
-        mDumpedProcessesCount++;
+        mDumpedProcessesCount.incrementAndGet();
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    /** Records the start of pid dumping to file (subject and criticalEventSection). */
+    /** Records the start of first pids dumping to file. */
     public void dumpingFirstPidsStarted() {
         mFirstPidsDumpingStartUptime = getUptimeMillis();
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingFirstPids");
     }
 
-    /** Records the end of pid dumping to file (subject and criticalEventSection). */
+    /** Records the end of first pids dumping to file. */
     public void dumpingFirstPidsEnded() {
         mFirstPidsDumpingDuration = getUptimeMillis() - mFirstPidsDumpingStartUptime;
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    /** Records the start of pid dumping to file (subject and criticalEventSection). */
+
+    /** Records the start of the copying of the pre-dumped first pid. */
+    public void copyingFirstPidStarted() {
+        mCopyingFirstPidStartUptime = getUptimeMillis();
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "copyingFirstPid");
+    }
+
+    /** Records the end of the copying of the pre-dumped first pid. */
+    public void copyingFirstPidEnded(boolean copySucceeded) {
+        mCopyingFirstPidDuration = getUptimeMillis() - mCopyingFirstPidStartUptime;
+        mCopyingFirstPidSucceeded = copySucceeded;
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /** Records the start of pre-dumping. */
+    public void dumpStackTracesTempFileStarted() {
+        mTempFileDumpingStartUptime = getUptimeMillis();
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpStackTracesTempFile");
+    }
+
+    /** Records the end of pre-dumping. */
+    public void dumpStackTracesTempFileEnded() {
+        mTempFileDumpingDuration = getUptimeMillis() - mTempFileDumpingStartUptime;
+        if (mEarlyDumpStatus == EarlyDumpStatus.UNKNOWN) {
+            mEarlyDumpStatus = EarlyDumpStatus.SUCCEEDED;
+        }
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /** Records file creation failure events in dumpStackTracesTempFile. */
+    public void dumpStackTracesTempFileCreationFailed() {
+        mEarlyDumpStatus = EarlyDumpStatus.FAILED_TO_CREATE_FILE;
+        Trace.instant(TRACE_TAG_ACTIVITY_MANAGER, "dumpStackTracesTempFileCreationFailed");
+    }
+
+    /** Records timeout events in dumpStackTracesTempFile. */
+    public void dumpStackTracesTempFileTimedOut() {
+        mEarlyDumpStatus = EarlyDumpStatus.TIMED_OUT;
+        Trace.instant(TRACE_TAG_ACTIVITY_MANAGER, "dumpStackTracesTempFileTimedOut");
+    }
+
+    /** Records the start of native pids dumping to file. */
     public void dumpingNativePidsStarted() {
         mNativePidsDumpingStartUptime = getUptimeMillis();
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingNativePids");
     }
 
-    /** Records the end of pid dumping to file (subject and criticalEventSection). */
+    /** Records the end of native pids dumping to file . */
     public void dumpingNativePidsEnded() {
         mNativePidsDumpingDuration =  getUptimeMillis() - mNativePidsDumpingStartUptime;
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    /** Records the start of pid dumping to file (subject and criticalEventSection). */
+    /** Records the start of extra pids dumping to file. */
     public void dumpingExtraPidsStarted() {
         mExtraPidsDumpingStartUptime = getUptimeMillis();
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingExtraPids");
     }
 
-    /** Records the end of pid dumping to file (subject and criticalEventSection). */
+    /** Records the end of extra pids dumping to file. */
     public void dumpingExtraPidsEnded() {
         mExtraPidsDumpingDuration =  getUptimeMillis() - mExtraPidsDumpingStartUptime;
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -337,7 +415,7 @@
      * Returns latency data as a comma separated value string for inclusion in ANR report.
      */
     public String dumpAsCommaSeparatedArrayWithHeader() {
-        return "DurationsV2: " + mAnrTriggerUptime
+        return "DurationsV3: " + mAnrTriggerUptime
                 /* triggering_to_app_not_responding_duration = */
                 + "," + (mAppNotRespondingStartUptime -  mAnrTriggerUptime)
                 /* app_not_responding_duration = */
@@ -370,7 +448,22 @@
                 /* anr_queue_size_when_pushed = */
                 + "," + mAnrQueueSize
                 /* dump_stack_traces_io_time = */
-                + "," + (mFirstPidsDumpingStartUptime - mDumpStackTracesStartUptime)
+                // We use copyingFirstPidUptime if we're dumping the durations list before the
+                // first pids ie after copying the early dump stacks.
+                + "," + ((mFirstPidsDumpingStartUptime > 0 ? mFirstPidsDumpingStartUptime
+                        : mCopyingFirstPidStartUptime) - mDumpStackTracesStartUptime)
+                /* temp_file_dump_duration = */
+                + "," + mTempFileDumpingDuration
+                /* temp_dump_request_on_queue_duration = */
+                + "," + (mTempFileDumpingStartUptime - mEarlyDumpRequestSubmissionUptime)
+                /* temp_dump_pid_count_when_pushed = */
+                + "," + mEarlyDumpExecutorPidCount
+                /* first_pid_copying_time = */
+                + "," + mCopyingFirstPidDuration
+                /* early_dump_status = */
+                + "," + mEarlyDumpStatus
+                /* copying_first_pid_succeeded = */
+                + "," + (mCopyingFirstPidSucceeded ? 1 : 0)
                 + "\n\n";
 
     }
@@ -449,7 +542,7 @@
 
             /* anr_queue_size_when_pushed = */ mAnrQueueSize,
             /* anr_type = */ mAnrType,
-            /* dumped_processes_count = */ mDumpedProcessesCount);
+            /* dumped_processes_count = */ mDumpedProcessesCount.get());
     }
 
     private void anrSkipped(String method) {
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 134a917..efaedd1 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -82,13 +82,6 @@
             }
             return mContentCaptureManager;
         }
-        // TODO(b/154191411): Try to revisit this issue in S.
-        // We use application to get DisplayManager here because ViewRootImpl holds reference of
-        // DisplayManager and implicitly holds reference of mContext, which makes activity cannot
-        // be GC'd even after destroyed if mContext is an activity object.
-        if (Context.DISPLAY_SERVICE.equals(name)) {
-            return super.getSystemService(name);
-        }
         // LayoutInflater and WallpaperManagerService should also be obtained from visual context
         // instead of base context.
         return (context != null) ? context.getSystemService(name) : super.getSystemService(name);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 47e6b6e..15f70f3 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -233,6 +233,7 @@
     private boolean mLastHasLeftStableInset = false;
     private int mLastWindowFlags = 0;
     private boolean mLastShouldAlwaysConsumeSystemBars = false;
+    private @InsetsType int mLastSuppressScrimTypes = 0;
 
     private int mRootScrollY = 0;
 
@@ -1143,6 +1144,7 @@
                 mLastHasLeftStableInset = hasLeftStableInset;
 
                 mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars();
+                mLastSuppressScrimTypes = insets.getSuppressScrimTypes();
             }
 
             boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
@@ -1378,7 +1380,8 @@
         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
                 mSemiTransparentBarColor, mWindow.mStatusBarColor,
                 appearance, APPEARANCE_LIGHT_STATUS_BARS,
-                mWindow.mEnsureStatusBarContrastWhenTransparent);
+                mWindow.mEnsureStatusBarContrastWhenTransparent
+                        && (mLastSuppressScrimTypes & WindowInsets.Type.statusBars()) == 0);
     }
 
     private int calculateNavigationBarColor(@Appearance int appearance) {
@@ -1386,7 +1389,7 @@
                 mSemiTransparentBarColor, mWindow.mNavigationBarColor,
                 appearance, APPEARANCE_LIGHT_NAVIGATION_BARS,
                 mWindow.mEnsureNavigationBarContrastWhenTransparent
-                        && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
+                        && (mLastSuppressScrimTypes & WindowInsets.Type.navigationBars()) == 0);
     }
 
     public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
diff --git a/core/java/com/android/internal/util/QuickSelect.java b/core/java/com/android/internal/util/QuickSelect.java
new file mode 100644
index 0000000..17739c9
--- /dev/null
+++ b/core/java/com/android/internal/util/QuickSelect.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An implementation of the quick selection algorithm as described in
+ * http://en.wikipedia.org/wiki/Quickselect.
+ *
+ * @hide
+ */
+public final class QuickSelect {
+    private static <T> int selectImpl(@NonNull List<T> list, int left, int right, int k,
+            @NonNull Comparator<? super T> comparator) {
+        while (true) {
+            if (left == right) {
+                return left;
+            }
+            final int pivotIndex = partition(list, left, right, (left + right) >> 1, comparator);
+            if (k == pivotIndex) {
+                return k;
+            } else if (k < pivotIndex) {
+                right = pivotIndex - 1;
+            } else {
+                left = pivotIndex + 1;
+            }
+        }
+    }
+
+    private static int selectImpl(@NonNull int[] array, int left, int right, int k) {
+        while (true) {
+            if (left == right) {
+                return left;
+            }
+            final int pivotIndex = partition(array, left, right, (left + right) >> 1);
+            if (k == pivotIndex) {
+                return k;
+            } else if (k < pivotIndex) {
+                right = pivotIndex - 1;
+            } else {
+                left = pivotIndex + 1;
+            }
+        }
+    }
+
+    private static int selectImpl(@NonNull long[] array, int left, int right, int k) {
+        while (true) {
+            if (left == right) {
+                return left;
+            }
+            final int pivotIndex = partition(array, left, right, (left + right) >> 1);
+            if (k == pivotIndex) {
+                return k;
+            } else if (k < pivotIndex) {
+                right = pivotIndex - 1;
+            } else {
+                left = pivotIndex + 1;
+            }
+        }
+    }
+
+    private static <T> int selectImpl(@NonNull T[] array, int left, int right, int k,
+            @NonNull Comparator<? super T> comparator) {
+        while (true) {
+            if (left == right) {
+                return left;
+            }
+            final int pivotIndex = partition(array, left, right, (left + right) >> 1, comparator);
+            if (k == pivotIndex) {
+                return k;
+            } else if (k < pivotIndex) {
+                right = pivotIndex - 1;
+            } else {
+                left = pivotIndex + 1;
+            }
+        }
+    }
+
+    private static <T> int partition(@NonNull List<T> list, int left, int right, int pivotIndex,
+            @NonNull Comparator<? super T> comparator) {
+        final T pivotValue = list.get(pivotIndex);
+        swap(list, right, pivotIndex);
+        int storeIndex = left;
+        for (int i = left; i < right; i++) {
+            if (comparator.compare(list.get(i), pivotValue) < 0) {
+                swap(list, storeIndex, i);
+                storeIndex++;
+            }
+        }
+        swap(list, right, storeIndex);
+        return storeIndex;
+    }
+
+    private static int partition(@NonNull int[] array, int left, int right, int pivotIndex) {
+        final int pivotValue = array[pivotIndex];
+        swap(array, right, pivotIndex);
+        int storeIndex = left;
+        for (int i = left; i < right; i++) {
+            if (array[i] < pivotValue) {
+                swap(array, storeIndex, i);
+                storeIndex++;
+            }
+        }
+        swap(array, right, storeIndex);
+        return storeIndex;
+    }
+
+    private static int partition(@NonNull long[] array, int left, int right, int pivotIndex) {
+        final long pivotValue = array[pivotIndex];
+        swap(array, right, pivotIndex);
+        int storeIndex = left;
+        for (int i = left; i < right; i++) {
+            if (array[i] < pivotValue) {
+                swap(array, storeIndex, i);
+                storeIndex++;
+            }
+        }
+        swap(array, right, storeIndex);
+        return storeIndex;
+    }
+
+    private static <T> int partition(@NonNull T[] array, int left, int right, int pivotIndex,
+            @NonNull Comparator<? super T> comparator) {
+        final T pivotValue = array[pivotIndex];
+        swap(array, right, pivotIndex);
+        int storeIndex = left;
+        for (int i = left; i < right; i++) {
+            if (comparator.compare(array[i], pivotValue) < 0) {
+                swap(array, storeIndex, i);
+                storeIndex++;
+            }
+        }
+        swap(array, right, storeIndex);
+        return storeIndex;
+    }
+
+    private static <T> void swap(@NonNull List<T> list, int left, int right) {
+        final T tmp = list.get(left);
+        list.set(left, list.get(right));
+        list.set(right, tmp);
+    }
+
+    private static void swap(@NonNull int[] array, int left, int right) {
+        final int tmp = array[left];
+        array[left] = array[right];
+        array[right] = tmp;
+    }
+
+    private static void swap(@NonNull long[] array, int left, int right) {
+        final long tmp = array[left];
+        array[left] = array[right];
+        array[right] = tmp;
+    }
+
+    private static <T> void swap(@NonNull T[] array, int left, int right) {
+        final T tmp = array[left];
+        array[left] = array[right];
+        array[right] = tmp;
+    }
+
+    /**
+     * Return the kth(0-based) smallest element from the given unsorted list.
+     *
+     * @param list The input list, it <b>will</b> be modified by the algorithm here.
+     * @param start The start offset of the list, inclusive.
+     * @param length The length of the sub list to be searched in.
+     * @param k The 0-based index.
+     * @param comparator The comparator which knows how to compare the elements in the list.
+     * @return The kth smallest element from the given list,
+     *         or IllegalArgumentException will be thrown if not found.
+     */
+    @Nullable
+    public static <T> T select(@NonNull List<T> list, int start, int length, int k,
+            @NonNull Comparator<? super T> comparator) {
+        if (list == null || start < 0 || length <= 0 || list.size() < start + length
+                || k < 0 || length <= k) {
+            throw new IllegalArgumentException();
+        }
+        return list.get(selectImpl(list, start, start + length - 1, k + start, comparator));
+    }
+
+    /**
+     * Return the kth(0-based) smallest element from the given unsorted array.
+     *
+     * @param array The input array, it <b>will</b> be modified by the algorithm here.
+     * @param start The start offset of the array, inclusive.
+     * @param length The length of the sub array to be searched in.
+     * @param k The 0-based index to search for.
+     * @return The kth smallest element from the given array,
+     *         or IllegalArgumentException will be thrown if not found.
+     */
+    public static int select(@NonNull int[] array, int start, int length, int k) {
+        if (array == null || start < 0 || length <= 0 || array.length < start + length
+                || k < 0 || length <= k) {
+            throw new IllegalArgumentException();
+        }
+        return array[selectImpl(array, start, start + length - 1, k + start)];
+    }
+
+    /**
+     * Return the kth(0-based) smallest element from the given unsorted array.
+     *
+     * @param array The input array, it <b>will</b> be modified by the algorithm here.
+     * @param start The start offset of the array, inclusive.
+     * @param length The length of the sub array to be searched in.
+     * @param k The 0-based index to search for.
+     * @return The kth smallest element from the given array,
+     *         or IllegalArgumentException will be thrown if not found.
+     */
+    public static long select(@NonNull long[] array, int start, int length, int k) {
+        if (array == null || start < 0 || length <= 0 || array.length < start + length
+                || k < 0 || length <= k) {
+            throw new IllegalArgumentException();
+        }
+        return array[selectImpl(array, start, start + length - 1, k + start)];
+    }
+
+    /**
+     * Return the kth(0-based) smallest element from the given unsorted array.
+     *
+     * @param array The input array, it <b>will</b> be modified by the algorithm here.
+     * @param start The start offset of the array, inclusive.
+     * @param length The length of the sub array to be searched in.
+     * @param k The 0-based index to search for.
+     * @param comparator The comparator which knows how to compare the elements in the list.
+     * @return The kth smallest element from the given array,
+     *         or IllegalArgumentException will be thrown if not found.
+     */
+    public static <T> T select(@NonNull T[] array, int start, int length, int k,
+            @NonNull Comparator<? super T> comparator) {
+        if (array == null || start < 0 || length <= 0 || array.length < start + length
+                || k < 0 || length <= k) {
+            throw new IllegalArgumentException();
+        }
+        return array[selectImpl(array, start, start + length - 1, k + start, comparator)];
+    }
+}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index dfcde3d..4065055 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -57,6 +57,7 @@
     void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle);
     int getCredentialType(int userId);
     int getPinLength(int userId);
+    boolean refreshStoredPinLength(int userId);
     byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
     void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in LockscreenCredential managedUserPassword);
     boolean getSeparateProfileChallengeEnabled(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 38632d1..fbad4b9 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -622,6 +622,24 @@
             return PIN_LENGTH_UNAVAILABLE;
         }
     }
+
+    /**
+     * This method saves the pin length value to disk based on the user's auto pin
+     * confirmation flag setting. If the auto pin confirmation flag is disabled, or if the
+     * user does not have a PIN setup, or if length of PIN is less than minimum storable PIN length
+     * value, the pin length value is set to PIN_LENGTH_UNAVAILABLE. Otherwise, if the
+     * flag is enabled, the pin length value is set to the actual length of the user's PIN.
+     * @param userId user id of the user whose pin length we want to save
+     * @return true/false depending on whether PIN length has been saved or not
+     */
+    public boolean refreshStoredPinLength(int userId) {
+        try {
+            return getLockSettings().refreshStoredPinLength(userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not store PIN length on disk " + e);
+            return false;
+        }
+    }
     /**
      * Records that the user has chosen a pattern at some time, even if the pattern is
      * currently cleared.
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6bec6bc..42d6896 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -334,7 +334,7 @@
                 "libtimeinstate",
                 "server_configurable_flags",
                 "libimage_io",
-                "libjpegrecoverymap",
+                "libultrahdr",
             ],
             export_shared_lib_headers: [
                 // our headers include libnativewindow's public headers
@@ -393,7 +393,7 @@
                 "libimage_io",
                 "libjpegdecoder",
                 "libjpegencoder",
-                "libjpegrecoverymap",
+                "libultrahdr",
             ],
         },
         host_linux: {
diff --git a/core/proto/android/companion/OWNERS b/core/proto/android/companion/OWNERS
new file mode 100644
index 0000000..edf9e56
--- /dev/null
+++ b/core/proto/android/companion/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/companion/OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 05b38a5..9a54500 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2917,6 +2917,14 @@
     <permission android:name="android.permission.BIND_SATELLITE_SERVICE"
         android:protectionLevel="signature|privileged|vendorPrivileged" />
 
+    <!-- Must be required by a SatelliteGatewayService to ensure that only the
+         system can bind to it.
+         <p>Protection level: signature
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by a telephony data service to ensure that only the
          system can bind to it.
          <p>Protection level: signature
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index fab7609..c7395dd 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -125,6 +125,10 @@
     <string name="config_satellite_service_package" translatable="false"></string>
     <java-symbol type="string" name="config_satellite_service_package" />
 
+    <!-- Telephony satellite gateway service package name to bind to by default. -->
+    <string name="config_satellite_gateway_service_package" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_gateway_service_package" />
+
     <!-- Telephony pointing UI package name to be launched. -->
     <string name="config_pointing_ui_package" translatable="false"></string>
     <java-symbol type="string" name="config_pointing_ui_package" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 947dc2d..362d859 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1970,8 +1970,8 @@
     <string name="face_error_user_canceled">Face Unlock canceled by user</string>
     <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
     <string name="face_error_lockout">Too many attempts. Try again later.</string>
-    <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=90] -->
-    <string name="face_error_lockout_permanent">Too many attempts. Face Unlock disabled.</string>
+    <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=100] -->
+    <string name="face_error_lockout_permanent">Too many attempts. Face Unlock unavailable.</string>
     <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=90] -->
     <string name="face_error_lockout_screen_lock">Too many attempts. Enter screen lock instead.</string>
     <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
@@ -5907,9 +5907,9 @@
     <string name="resolver_no_personal_apps_available">No personal apps</string>
 
     <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
-    <string name="miniresolver_open_in_personal">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your personal profile?</string>
+    <string name="miniresolver_open_in_personal">Open personal <xliff:g id="app" example="YouTube">%s</xliff:g></string>
     <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
-    <string name="miniresolver_open_in_work">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your work profile?</string>
+    <string name="miniresolver_open_in_work">Open work <xliff:g id="app" example="YouTube">%s</xliff:g></string>
     <!-- Button option. Open the link in the personal browser. [CHAR LIMIT=NONE] -->
     <string name="miniresolver_use_personal_browser">Use personal browser</string>
     <!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index a0d8dcf..ba6c8fa 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -122,6 +122,22 @@
         }
     }
 
+    @SmallTest
+    fun testIsNonLinearFontScalingActive() {
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1f)).isFalse()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0f)).isFalse()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(-1f)).isFalse()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0.85f)).isFalse()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.02f)).isFalse()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.10f)).isFalse()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.15f)).isTrue()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.1499999f))
+                .isTrue()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.5f)).isTrue()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(2f)).isTrue()
+        assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(3f)).isTrue()
+    }
+
     @LargeTest
     @Test
     fun allFeasibleScalesAndConversionsDoNotCrash() {
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index 9a202ae..1075d44 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -21,8 +21,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -38,6 +41,8 @@
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 
+import com.android.internal.R;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -50,6 +55,7 @@
 import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 @Presubmit
@@ -70,6 +76,8 @@
     private IFaceService mService;
     @Mock
     private FaceManager.AuthenticationCallback mAuthCallback;
+    @Mock
+    private FaceManager.EnrollmentCallback mEnrollmentCallback;
 
     @Captor
     private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor;
@@ -107,9 +115,7 @@
 
     @Test
     public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException {
-        verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
-
-        mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+        initializeProperties();
         List<FaceSensorPropertiesInternal> actual = mFaceManager.getSensorPropertiesInternal();
 
         assertThat(actual).containsExactlyElementsIn(mProps);
@@ -148,4 +154,33 @@
 
         verify(mAuthCallback).onAuthenticationError(eq(FACE_ERROR_HW_UNAVAILABLE), any());
     }
+
+    @Test
+    public void enrollment_errorWhenFaceEnrollmentExists() throws RemoteException {
+        when(mResources.getInteger(R.integer.config_faceMaxTemplatesPerUser)).thenReturn(1);
+        when(mService.getEnrolledFaces(anyInt(), anyInt(), anyString()))
+                .thenReturn(Collections.emptyList())
+                .thenReturn(Collections.singletonList(new Face("Face" /* name */, 0 /* faceId */,
+                        0 /* deviceId */)));
+
+        initializeProperties();
+        mFaceManager.enroll(USER_ID, new byte[]{},
+                new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
+
+        verify(mService).enroll(eq(USER_ID), any(), any(), any(), anyString(), any(), any(),
+                anyBoolean());
+
+        mFaceManager.enroll(USER_ID, new byte[]{},
+                new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
+
+        verify(mService, atMost(1 /* maxNumberOfInvocations */)).enroll(eq(USER_ID), any(), any(),
+                any(), anyString(), any(), any(), anyBoolean());
+        verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_HW_UNAVAILABLE), anyString());
+    }
+
+    private void initializeProperties() throws RemoteException {
+        verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+
+        mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+    }
 }
diff --git a/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java b/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
index 4eea076..7d5a0364 100644
--- a/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
+++ b/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
@@ -59,7 +59,10 @@
             .thenReturn(175L)
             .thenReturn(198L)
             .thenReturn(203L)
-            .thenReturn(209L);
+            .thenReturn(209L)
+            .thenReturn(211L)
+            .thenReturn(212L)
+            .thenReturn(220L);
     }
 
     @Test
@@ -68,6 +71,7 @@
         mLatencyTracker.appNotRespondingStarted();
         mLatencyTracker.waitingOnAnrRecordLockStarted();
         mLatencyTracker.waitingOnAnrRecordLockEnded();
+        mLatencyTracker.earlyDumpRequestSubmittedWithSize(5);
         mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
         mLatencyTracker.appNotRespondingEnded();
 
@@ -90,7 +94,16 @@
         mLatencyTracker.waitingOnProcLockStarted();
         mLatencyTracker.waitingOnProcLockEnded();
 
+        mLatencyTracker.dumpStackTracesTempFileStarted();
+        mLatencyTracker.dumpingPidStarted(5);
+
         mLatencyTracker.dumpStackTracesStarted();
+        mLatencyTracker.copyingFirstPidStarted();
+
+        mLatencyTracker.dumpingPidEnded();
+        mLatencyTracker.dumpStackTracesTempFileEnded();
+
+        mLatencyTracker.copyingFirstPidEnded(true);
         mLatencyTracker.dumpingFirstPidsStarted();
         mLatencyTracker.dumpingPidStarted(1);
         mLatencyTracker.dumpingPidEnded();
@@ -111,7 +124,7 @@
         mLatencyTracker.close();
 
         assertThat(mLatencyTracker.dumpAsCommaSeparatedArrayWithHeader())
-            .isEqualTo("DurationsV2: 50,5,25,8,115,2,3,7,8,15,2,7,23,10,3,6\n\n");
+            .isEqualTo("DurationsV3: 50,5,33,11,112,4,2,4,6,5,1,10,5,10,3,9,11,129,5,8,1\n\n");
         verify(mLatencyTracker, times(1)).pushAtom();
     }
 
@@ -121,6 +134,7 @@
         mLatencyTracker.appNotRespondingStarted();
         mLatencyTracker.waitingOnAnrRecordLockStarted();
         mLatencyTracker.waitingOnAnrRecordLockEnded();
+        mLatencyTracker.earlyDumpRequestSubmittedWithSize(5);
         mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
         mLatencyTracker.appNotRespondingEnded();
 
@@ -143,7 +157,18 @@
         mLatencyTracker.waitingOnProcLockStarted();
         mLatencyTracker.waitingOnProcLockEnded();
 
+
+
+        mLatencyTracker.dumpStackTracesTempFileStarted();
+        mLatencyTracker.dumpingPidStarted(5);
+
         mLatencyTracker.dumpStackTracesStarted();
+        mLatencyTracker.copyingFirstPidStarted();
+
+        mLatencyTracker.dumpingPidEnded();
+        mLatencyTracker.dumpStackTracesTempFileEnded();
+
+        mLatencyTracker.copyingFirstPidEnded(true);
         mLatencyTracker.dumpingFirstPidsStarted();
         mLatencyTracker.dumpingPidStarted(1);
         mLatencyTracker.dumpingPidEnded();
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 4fed396..b4ba23c 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -40,14 +40,14 @@
     @Test
     public void systemWindowInsets_afterConsuming_isConsumed() {
         assertTrue(new WindowInsets(WindowInsets.createCompatTypeMap(new Rect(1, 2, 3, 4)), null,
-                null, false, false, null, null, null, null,
+                null, false, false, 0, null, null, null, null,
                 WindowInsets.Type.systemBars(), false)
                 .consumeSystemWindowInsets().isConsumed());
     }
 
     @Test
     public void multiNullConstructor_isConsumed() {
-        assertTrue(new WindowInsets(null, null, null, false, false, null, null, null, null,
+        assertTrue(new WindowInsets(null, null, null, false, false, 0, null, null, null, null,
                 WindowInsets.Type.systemBars(), false).isConsumed());
     }
 
@@ -63,8 +63,8 @@
         boolean[] visible = new boolean[SIZE];
         WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
         WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
-        WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false, null,
-                null, null, DisplayShape.NONE, systemBars(),
+        WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false,
+                0, null, null, null, DisplayShape.NONE, systemBars(),
                 true /* compatIgnoreVisibility */);
         assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
     }
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index 4d4ec35..a1a4265 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -169,7 +169,7 @@
 
     private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
         return new WindowInsets(WindowInsets.createCompatTypeMap(content.toRect()), null, null,
-                false, false, cutout, null, null, null, WindowInsets.Type.systemBars(), false);
+                false, false, 0, cutout, null, null, null, WindowInsets.Type.systemBars(), false);
     }
 
     private ViewGroup createViewGroupWithId(int id) {
diff --git a/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java
new file mode 100644
index 0000000..1b9d2ef
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link QuickSelect}.
+ */
+public final class QuickSelectTest extends TestCase {
+
+    public void testQuickSelect() throws Exception {
+        test((List<Integer>) null, 0, null);
+        test(Arrays.asList(), -1, null);
+        test(Arrays.asList(), 0, null);
+        test(Arrays.asList(), 1, null);
+        test(Arrays.asList(1), -1, 1, 0, null);
+        test(Arrays.asList(1), 1, -1, 0, null);
+        test(Arrays.asList(1), 0, 1, -1, null);
+        test(Arrays.asList(1), 1, 1, 0, null);
+        test(Arrays.asList(1), 0, 1);
+        test(Arrays.asList(1), 1, null);
+        test(Arrays.asList(1, 2, 3, 4, 5), 0, 1);
+        test(Arrays.asList(1, 2, 3, 4, 5), 1, 2);
+        test(Arrays.asList(1, 2, 3, 4, 5), 2, 3);
+        test(Arrays.asList(1, 2, 3, 4, 5), 3, 4);
+        test(Arrays.asList(1, 2, 3, 4, 5), 4, 5);
+        test(Arrays.asList(1, 2, 3, 4, 5), 5, null);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 2, 7);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 4, 9);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 7, 20);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 8, null);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 0, 3);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 1, 4);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 2, 10);
+        test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 3, null);
+
+        test((int[]) null, 0, null);
+        test(new int[0], -1, null);
+        test(new int[0], 0, null);
+        test(new int[0], 1, null);
+        test(new int[] {1}, -1, 1, 0, null);
+        test(new int[] {1}, 1, -1, 0, null);
+        test(new int[] {1}, 1, 0, -1, null);
+        test(new int[] {1}, 1, 1, 0, null);
+        test(new int[] {1}, 0, 1);
+        test(new int[] {1}, 1, null);
+        test(new int[] {1, 2, 3, 4, 5}, 0, 1);
+        test(new int[] {1, 2, 3, 4, 5}, 1, 2);
+        test(new int[] {1, 2, 3, 4, 5}, 2, 3);
+        test(new int[] {1, 2, 3, 4, 5}, 3, 4);
+        test(new int[] {1, 2, 3, 4, 5}, 4, 5);
+        test(new int[] {1, 2, 3, 4, 5}, 5, null);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 2, 7);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 4, 9);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 7, 20);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 8, null);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 0, 3);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 1, 4);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 2, 10);
+        test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 3, null);
+
+        test((long[]) null, 0, null);
+        test(new long[0], -1, null);
+        test(new long[0], 0, null);
+        test(new long[0], 1, null);
+        test(new long[] {1}, -1, 1, 0, null);
+        test(new long[] {1}, 1, -1, 0, null);
+        test(new long[] {1}, 1, 0, -1, null);
+        test(new long[] {1}, 1, 1, 0, null);
+        test(new long[] {1}, 0, 1L);
+        test(new long[] {1}, 1, null);
+        test(new long[] {1, 2, 3, 4, 5}, 0, 1L);
+        test(new long[] {1, 2, 3, 4, 5}, 1, 2L);
+        test(new long[] {1, 2, 3, 4, 5}, 2, 3L);
+        test(new long[] {1, 2, 3, 4, 5}, 3, 4L);
+        test(new long[] {1, 2, 3, 4, 5}, 4, 5L);
+        test(new long[] {1, 2, 3, 4, 5}, 5, null);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 2, 7L);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 4, 9L);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 7, 20L);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 8, null);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 0, 3L);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 1, 4L);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 2, 10L);
+        test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 3, null);
+    }
+
+    private void test(List<Integer> input, int k, Integer expected) throws Exception {
+        test(input, 0, input == null ? 0 : input.size(), k, expected);
+    }
+
+    private void test(List<Integer> input, int start, int length, int k, Integer expected)
+            throws Exception {
+        try {
+            final Integer result = QuickSelect.select(input, start, length, k, Integer::compare);
+            assertEquals(expected, result);
+        } catch (IllegalArgumentException e) {
+            if (expected != null) {
+                throw new Exception(e);
+            }
+        }
+    }
+
+    private void test(int[] input, int k, Integer expected) throws Exception {
+        test(input, 0, input == null ? 0 : input.length, k, expected);
+    }
+
+    private void test(int[] input, int start, int length, int k, Integer expected)
+            throws Exception {
+        try {
+            final int result = QuickSelect.select(input, start, length, k);
+            assertEquals((int) expected, result);
+        } catch (IllegalArgumentException e) {
+            if (expected != null) {
+                throw new Exception(e);
+            }
+        }
+    }
+
+    private void test(long[] input, int k, Long expected) throws Exception {
+        test(input, 0, input == null ? 0 : input.length, k, expected);
+    }
+
+    private void test(long[] input, int start, int length, int k, Long expected) throws Exception {
+        try {
+            final long result = QuickSelect.select(input, start, length, k);
+            assertEquals((long) expected, result);
+        } catch (IllegalArgumentException e) {
+            if (expected != null) {
+                throw new Exception(e);
+            }
+        }
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0faf62e..8ece337 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -122,6 +122,7 @@
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
         <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
         <permission name="android.permission.BIND_IMS_SERVICE"/>
+        <permission name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE"/>
         <permission name="android.permission.BIND_SATELLITE_SERVICE"/>
         <permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
         <permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
@@ -515,6 +516,8 @@
         <permission name="android.permission.READ_RESTRICTED_STATS"/>
         <!-- Permission required for CTS test -->
         <permission name="android.permission.LOG_FOREGROUND_RESOURCE_USE"/>
+        <!-- Permission required for CTS test - CtsVoiceInteractionTestCases -->
+        <permission name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index f6b21ba..fb1980a 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -31,8 +31,7 @@
         android:orientation="horizontal"
         android:clickable="true"
         android:focusable="true"
-        android:paddingStart="8dp"
-        android:background="?android:selectableItemBackgroundBorderless">
+        android:paddingStart="8dp">
 
         <ImageView
             android:id="@+id/application_icon"
@@ -88,6 +87,6 @@
         android:src="@drawable/decor_close_button_dark"
         android:scaleType="fitCenter"
         android:gravity="end"
-        android:background="?android:selectableItemBackgroundBorderless"
+        android:background="@null"
         android:tint="@color/desktop_mode_caption_close_button_dark"/>
 </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 026ea069..d3f3958 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -47,7 +47,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.launcher3.icons.BubbleBadgeIconFactory;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
@@ -65,7 +64,11 @@
 public class Bubble implements BubbleViewProvider {
     private static final String TAG = "Bubble";
 
-    public static final String KEY_APP_BUBBLE = "key_app_bubble";
+    /** A string suffix used in app bubbles' {@link #mKey}. */
+    private static final String KEY_APP_BUBBLE = "key_app_bubble";
+
+    /** Whether the bubble is an app bubble. */
+    private final boolean mIsAppBubble;
 
     private final String mKey;
     @Nullable
@@ -182,7 +185,7 @@
     private PendingIntent mDeleteIntent;
 
     /**
-     * Used only for a special bubble in the stack that has the key {@link #KEY_APP_BUBBLE}.
+     * Used only for a special bubble in the stack that has {@link #mIsAppBubble} set to true.
      * There can only be one of these bubbles in the stack and this intent will be populated for
      * that bubble.
      */
@@ -217,24 +220,54 @@
         mMainExecutor = mainExecutor;
         mTaskId = taskId;
         mBubbleMetadataFlagListener = listener;
+        mIsAppBubble = false;
     }
 
-    public Bubble(Intent intent,
+    private Bubble(
+            Intent intent,
             UserHandle user,
             @Nullable Icon icon,
+            boolean isAppBubble,
+            String key,
             Executor mainExecutor) {
-        mKey = KEY_APP_BUBBLE;
         mGroupKey = null;
         mLocusId = null;
         mFlags = 0;
         mUser = user;
         mIcon = icon;
+        mIsAppBubble = isAppBubble;
+        mKey = key;
         mShowBubbleUpdateDot = false;
         mMainExecutor = mainExecutor;
         mTaskId = INVALID_TASK_ID;
         mAppIntent = intent;
         mDesiredHeight = Integer.MAX_VALUE;
         mPackageName = intent.getPackage();
+
+    }
+
+    /** Creates an app bubble. */
+    public static Bubble createAppBubble(
+            Intent intent,
+            UserHandle user,
+            @Nullable Icon icon,
+            Executor mainExecutor) {
+        return new Bubble(intent,
+                user,
+                icon,
+                /* isAppBubble= */ true,
+                /* key= */ getAppBubbleKeyForApp(intent.getPackage(), user),
+                mainExecutor);
+    }
+
+    /**
+     * Returns the key for an app bubble from an app with package name, {@code packageName} on an
+     * Android user, {@code user}.
+     */
+    public static String getAppBubbleKeyForApp(String packageName, UserHandle user) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(user);
+        return KEY_APP_BUBBLE + ":" + user.getIdentifier()  + ":" + packageName;
     }
 
     @VisibleForTesting(visibility = PRIVATE)
@@ -242,6 +275,7 @@
             final Bubbles.BubbleMetadataFlagListener listener,
             final Bubbles.PendingIntentCanceledListener intentCancelListener,
             Executor mainExecutor) {
+        mIsAppBubble = false;
         mKey = entry.getKey();
         mGroupKey = entry.getGroupKey();
         mLocusId = entry.getLocusId();
@@ -436,7 +470,6 @@
      * @param stackView the view the bubble is added to, iff showing as floating.
      * @param layerView the layer the bubble is added to, iff showing in the bubble bar.
      * @param iconFactory the icon factory use to create images for the bubble.
-     * @param badgeIconFactory the icon factory to create app badges for the bubble.
      */
     void inflate(BubbleViewInfoTask.Callback callback,
             Context context,
@@ -444,7 +477,6 @@
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory iconFactory,
-            BubbleBadgeIconFactory badgeIconFactory,
             boolean skipInflation) {
         if (isBubbleLoading()) {
             mInflationTask.cancel(true /* mayInterruptIfRunning */);
@@ -455,7 +487,6 @@
                 stackView,
                 layerView,
                 iconFactory,
-                badgeIconFactory,
                 skipInflation,
                 callback,
                 mMainExecutor);
@@ -819,7 +850,7 @@
     }
 
     boolean isAppBubble() {
-        return KEY_APP_BUBBLE.equals(mKey);
+        return mIsAppBubble;
     }
 
     Intent getSettingsIntent(final Context context) {
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 fd66153..21f02b1 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
@@ -24,7 +24,6 @@
 import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -89,7 +88,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.launcher3.icons.BubbleBadgeIconFactory;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -209,7 +207,6 @@
     @Nullable private BubbleStackView mStackView;
     @Nullable private BubbleBarLayerView mLayerView;
     private BubbleIconFactory mBubbleIconFactory;
-    private BubbleBadgeIconFactory mBubbleBadgeIconFactory;
     private BubblePositioner mBubblePositioner;
     private Bubbles.SysuiProxy mSysuiProxy;
 
@@ -321,8 +318,7 @@
         mBubbleData = data;
         mSavedUserBubbleData = new SparseArray<>();
         mBubbleIconFactory = new BubbleIconFactory(context,
-                context.getResources().getDimensionPixelSize(R.dimen.bubble_size));
-        mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context,
+                context.getResources().getDimensionPixelSize(R.dimen.bubble_size),
                 context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
                 context.getResources().getColor(R.color.important_conversation),
                 context.getResources().getDimensionPixelSize(
@@ -700,6 +696,10 @@
         return mBubblePositioner;
     }
 
+    BubbleIconFactory getIconFactory() {
+        return mBubbleIconFactory;
+    }
+
     public Bubbles.SysuiProxy getSysuiProxy() {
         return mSysuiProxy;
     }
@@ -936,8 +936,7 @@
             mStackView.onThemeChanged();
         }
         mBubbleIconFactory = new BubbleIconFactory(mContext,
-                mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size));
-        mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext,
+                mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size),
                 mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
                 mContext.getResources().getColor(R.color.important_conversation),
                 mContext.getResources().getDimensionPixelSize(
@@ -951,7 +950,6 @@
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
-                    mBubbleBadgeIconFactory,
                     false /* skipInflation */);
         }
         for (Bubble b : mBubbleData.getOverflowBubbles()) {
@@ -961,7 +959,6 @@
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
-                    mBubbleBadgeIconFactory,
                     false /* skipInflation */);
         }
     }
@@ -978,8 +975,7 @@
                 mScreenBounds.set(newConfig.windowConfiguration.getBounds());
                 mBubbleData.onMaxBubblesChanged();
                 mBubbleIconFactory = new BubbleIconFactory(mContext,
-                        mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size));
-                mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext,
+                        mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size),
                         mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
                         mContext.getResources().getColor(R.color.important_conversation),
                         mContext.getResources().getDimensionPixelSize(
@@ -1196,14 +1192,15 @@
             return;
         }
 
+        String appBubbleKey = Bubble.getAppBubbleKeyForApp(intent.getPackage(), user);
         PackageManager packageManager = getPackageManagerForUser(mContext, user.getIdentifier());
-        if (!isResizableActivity(intent, packageManager, KEY_APP_BUBBLE)) return;
+        if (!isResizableActivity(intent, packageManager, appBubbleKey)) return;
 
-        Bubble existingAppBubble = mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE);
+        Bubble existingAppBubble = mBubbleData.getBubbleInStackWithKey(appBubbleKey);
         if (existingAppBubble != null) {
             BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
             if (isStackExpanded()) {
-                if (selectedBubble != null && KEY_APP_BUBBLE.equals(selectedBubble.getKey())) {
+                if (selectedBubble != null && appBubbleKey.equals(selectedBubble.getKey())) {
                     // App bubble is expanded, lets collapse
                     collapseStack();
                 } else {
@@ -1217,7 +1214,7 @@
             }
         } else {
             // App bubble does not exist, lets add and expand it
-            Bubble b = new Bubble(intent, user, icon, mMainExecutor);
+            Bubble b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
             b.setShouldAutoExpand(true);
             inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
         }
@@ -1250,8 +1247,8 @@
     }
 
     /** Sets the app bubble's taskId which is cached for SysUI. */
-    public void setAppBubbleTaskId(int taskId) {
-        mImpl.mCachedState.setAppBubbleTaskId(taskId);
+    public void setAppBubbleTaskId(String key, int taskId) {
+        mImpl.mCachedState.setAppBubbleTaskId(key, taskId);
     }
 
     /**
@@ -1275,7 +1272,6 @@
                         mStackView,
                         mLayerView,
                         mBubbleIconFactory,
-                        mBubbleBadgeIconFactory,
                         true /* skipInflation */);
             });
             return null;
@@ -1351,7 +1347,7 @@
         bubble.setInflateSynchronously(mInflateSynchronously);
         bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
                 mContext, this, mStackView,  mLayerView,
-                mBubbleIconFactory, mBubbleBadgeIconFactory,
+                mBubbleIconFactory,
                 false /* skipInflation */);
     }
 
@@ -2049,7 +2045,8 @@
             private HashSet<String> mSuppressedBubbleKeys = new HashSet<>();
             private HashMap<String, String> mSuppressedGroupToNotifKeys = new HashMap<>();
             private HashMap<String, Bubble> mShortcutIdToBubble = new HashMap<>();
-            private int mAppBubbleTaskId = INVALID_TASK_ID;
+
+            private HashMap<String, Integer> mAppBubbleTaskIds = new HashMap();
 
             private ArrayList<Bubble> mTmpBubbles = new ArrayList<>();
 
@@ -2081,20 +2078,20 @@
 
                 mSuppressedBubbleKeys.clear();
                 mShortcutIdToBubble.clear();
-                mAppBubbleTaskId = INVALID_TASK_ID;
+                mAppBubbleTaskIds.clear();
                 for (Bubble b : mTmpBubbles) {
                     mShortcutIdToBubble.put(b.getShortcutId(), b);
                     updateBubbleSuppressedState(b);
 
-                    if (KEY_APP_BUBBLE.equals(b.getKey())) {
-                        mAppBubbleTaskId = b.getTaskId();
+                    if (b.isAppBubble()) {
+                        mAppBubbleTaskIds.put(b.getKey(), b.getTaskId());
                     }
                 }
             }
 
             /** Sets the app bubble's taskId which is cached for SysUI. */
-            synchronized void setAppBubbleTaskId(int taskId) {
-                mAppBubbleTaskId = taskId;
+            synchronized void setAppBubbleTaskId(String key, int taskId) {
+                mAppBubbleTaskIds.put(key, taskId);
             }
 
             /**
@@ -2147,7 +2144,7 @@
                     pw.println("   suppressing: " + key);
                 }
 
-                pw.print("mAppBubbleTaskId: " + mAppBubbleTaskId);
+                pw.print("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values());
             }
         }
 
@@ -2209,7 +2206,7 @@
 
         @Override
         public boolean isAppBubbleTaskId(int taskId) {
-            return mCachedState.mAppBubbleTaskId == taskId;
+            return mCachedState.mAppBubbleTaskIds.values().contains(taskId);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index f9cf9d3..cc8f50e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -17,7 +17,6 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -38,6 +37,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubbles.DismissReason;
 import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
@@ -779,7 +779,7 @@
                 || !(reason == Bubbles.DISMISS_AGED
                 || reason == Bubbles.DISMISS_USER_GESTURE
                 || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)
-                || KEY_APP_BUBBLE.equals(bubble.getKey())) {
+                || bubble.isAppBubble()) {
             return;
         }
         if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index a317c44..6c482c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -246,7 +246,7 @@
                                 mBubble.getAppBubbleIntent()
                                         .addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
                                         .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
-                                PendingIntent.FLAG_IMMUTABLE,
+                                PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
                                 /* options= */ null);
                         mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
                                 launchBounds);
@@ -287,9 +287,9 @@
             // The taskId is saved to use for removeTask, preventing appearance in recent tasks.
             mTaskId = taskId;
 
-            if (Bubble.KEY_APP_BUBBLE.equals(getBubbleKey())) {
+            if (mBubble != null && mBubble.isAppBubble()) {
                 // Let the controller know sooner what the taskId is.
-                mController.setAppBubbleTaskId(mTaskId);
+                mController.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
             }
 
             // With the task org, the taskAppeared callback will only happen once the task has
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index c2a05b7..c49451d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -95,7 +95,11 @@
         overflowBtn?.iconDrawable?.setTint(shapeColor)
 
         val iconFactory = BubbleIconFactory(context,
-                context.getResources().getDimensionPixelSize(R.dimen.bubble_size))
+                context.getResources().getDimensionPixelSize(R.dimen.bubble_size),
+                context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
+                context.getResources().getColor(R.color.important_conversation),
+                context.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.importance_ring_stroke_width))
 
         // Update bitmap
         val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index d1081de..8ab9841 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -42,7 +42,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.BubbleBadgeIconFactory;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
@@ -75,7 +74,6 @@
     private WeakReference<BubbleStackView> mStackView;
     private WeakReference<BubbleBarLayerView> mLayerView;
     private BubbleIconFactory mIconFactory;
-    private BubbleBadgeIconFactory mBadgeIconFactory;
     private boolean mSkipInflation;
     private Callback mCallback;
     private Executor mMainExecutor;
@@ -90,7 +88,6 @@
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory factory,
-            BubbleBadgeIconFactory badgeFactory,
             boolean skipInflation,
             Callback c,
             Executor mainExecutor) {
@@ -100,7 +97,6 @@
         mStackView = new WeakReference<>(stackView);
         mLayerView = new WeakReference<>(layerView);
         mIconFactory = factory;
-        mBadgeIconFactory = badgeFactory;
         mSkipInflation = skipInflation;
         mCallback = c;
         mMainExecutor = mainExecutor;
@@ -110,10 +106,10 @@
     protected BubbleViewInfo doInBackground(Void... voids) {
         if (mController.get().isShowingAsBubbleBar()) {
             return BubbleViewInfo.populateForBubbleBar(mContext.get(), mController.get(),
-                    mLayerView.get(), mBadgeIconFactory, mBubble, mSkipInflation);
+                    mLayerView.get(), mIconFactory, mBubble, mSkipInflation);
         } else {
             return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(),
-                    mIconFactory, mBadgeIconFactory, mBubble, mSkipInflation);
+                    mIconFactory, mBubble, mSkipInflation);
         }
     }
 
@@ -156,7 +152,7 @@
 
         @Nullable
         public static BubbleViewInfo populateForBubbleBar(Context c, BubbleController controller,
-                BubbleBarLayerView layerView, BubbleBadgeIconFactory badgeIconFactory, Bubble b,
+                BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b,
                 boolean skipInflation) {
             BubbleViewInfo info = new BubbleViewInfo();
 
@@ -195,7 +191,7 @@
                 return null;
             }
 
-            info.rawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
+            info.rawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon;
 
             return info;
         }
@@ -203,8 +199,7 @@
         @VisibleForTesting
         @Nullable
         public static BubbleViewInfo populate(Context c, BubbleController controller,
-                BubbleStackView stackView, BubbleIconFactory iconFactory,
-                BubbleBadgeIconFactory badgeIconFactory, Bubble b,
+                BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b,
                 boolean skipInflation) {
             BubbleViewInfo info = new BubbleViewInfo();
 
@@ -256,16 +251,16 @@
                 bubbleDrawable = appIcon;
             }
 
-            BitmapInfo badgeBitmapInfo = badgeIconFactory.getBadgeBitmap(badgedIcon,
+            BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
                     b.isImportantConversation());
             info.badgeBitmap = badgeBitmapInfo.icon;
             // Raw badge bitmap never includes the important conversation ring
             info.rawBadgeBitmap = b.isImportantConversation()
-                    ? badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon
+                    ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
                     : badgeBitmapInfo.icon;
 
             float[] bubbleBitmapScale = new float[1];
-            info.bubbleBitmap = iconFactory.createIconBitmap(bubbleDrawable, bubbleBitmapScale);
+            info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
 
             // Dot color & placement
             Path iconPath = PathParser.createPathFromPathData(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 4970fa0..56616cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -165,6 +165,10 @@
             t.remove(mGapBackgroundLeash);
             mGapBackgroundLeash = null;
         }
+        if (mScreenshot != null) {
+            t.remove(mScreenshot);
+            mScreenshot = null;
+        }
         mHostLeash = null;
         mIcon = null;
         mResizingIconView = null;
@@ -324,6 +328,8 @@
         if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) {
             if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
                 mScreenshotAnimator.cancel();
+            } else if (mScreenshot != null) {
+                t.remove(mScreenshot);
             }
 
             mTempRect.set(mOldBounds);
@@ -340,6 +346,8 @@
         if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) {
             if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
                 mScreenshotAnimator.cancel();
+            } else if (mScreenshot != null) {
+                t.remove(mScreenshot);
             }
 
             mScreenshot = screenshot;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index d04ce15..713ace6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -146,13 +146,17 @@
             t.apply();
 
             // execute the runnable if non-null after WCT is applied to finish resizing pip
-            if (mPipFinishResizeWCTRunnable != null) {
-                mPipFinishResizeWCTRunnable.run();
-                mPipFinishResizeWCTRunnable = null;
-            }
+            maybePerformFinishResizeCallback();
         }
     };
 
+    private void maybePerformFinishResizeCallback() {
+        if (mPipFinishResizeWCTRunnable != null) {
+            mPipFinishResizeWCTRunnable.run();
+            mPipFinishResizeWCTRunnable = null;
+        }
+    }
+
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
             new PipAnimationController.PipAnimationCallback() {
@@ -1007,6 +1011,7 @@
             return;
         }
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mPipTransitionController.onFixedRotationFinished();
             clearWaitForFixedRotation();
             return;
         }
@@ -1606,6 +1611,10 @@
         if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
             mSplitScreenOptional.ifPresent(splitScreenController ->
                     splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
+        } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+            // when leaving PiP we can call the callback without sync
+            maybePerformFinishResizeCallback();
+            mTaskOrganizer.applyTransaction(wct);
         } else {
             mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 5755a10..99cb6f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -409,9 +409,31 @@
 
     @Override
     public void onFixedRotationStarted() {
+        fadeEnteredPipIfNeed(false /* show */);
+    }
+
+    @Override
+    public void onFixedRotationFinished() {
+        fadeEnteredPipIfNeed(true /* show */);
+    }
+
+    private void fadeEnteredPipIfNeed(boolean show) {
         // The transition with this fixed rotation may be handled by other handler before reaching
         // PipTransition, so we cannot do this in #startAnimation.
-        if (mPipTransitionState.getTransitionState() == ENTERED_PIP && !mHasFadeOut) {
+        if (!mPipTransitionState.hasEnteredPip()) {
+            return;
+        }
+        if (show && mHasFadeOut) {
+            // If there is a pending transition, then let startAnimation handle it. And if it is
+            // handled, mHasFadeOut will be set to false and this runnable will be no-op. Otherwise
+            // make sure the PiP will reshow, e.g. swipe-up with fixed rotation (fade-out) but
+            // return to the current app (only finish the recent transition).
+            mTransitions.runOnIdle(() -> {
+                if (mHasFadeOut && mPipTransitionState.hasEnteredPip()) {
+                    fadeExistingPip(true /* show */);
+                }
+            });
+        } else if (!show && !mHasFadeOut) {
             // Fade out the existing PiP to avoid jump cut during seamless rotation.
             fadeExistingPip(false /* show */);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index ff7ab8b..949d6f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -123,6 +123,10 @@
     public void onFixedRotationStarted() {
     }
 
+    /** Called when the fixed rotation finished. */
+    public void onFixedRotationFinished() {
+    }
+
     public PipTransitionController(
             @NonNull ShellInit shellInit,
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index c8d6a5e8..a8b209f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -213,12 +213,7 @@
 
         RecentsController(IRecentsAnimationRunner listener) {
             mListener = listener;
-            mDeathHandler = () -> mExecutor.execute(() -> {
-                if (mListener == null) return;
-                if (mFinishCB != null) {
-                    finish(mWillFinishToHome, false /* leaveHint */);
-                }
-            });
+            mDeathHandler = () -> finish(mWillFinishToHome, false /* leaveHint */);
             try {
                 mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
             } catch (RemoteException e) {
@@ -245,7 +240,7 @@
                 }
             }
             if (mFinishCB != null) {
-                finish(toHome, false /* userLeave */);
+                finishInner(toHome, false /* userLeave */);
             } else {
                 cleanUp();
             }
@@ -540,20 +535,25 @@
                 mergeActivityOnly(info, t);
             } else if (!didMergeThings) {
                 // Didn't recognize anything in incoming transition so don't merge it.
-                Slog.w(TAG, "Don't know how to merge this transition.");
+                Slog.w(TAG, "Don't know how to merge this transition, foundRecentsClosing="
+                        + foundRecentsClosing);
+                if (foundRecentsClosing) {
+                    mWillFinishToHome = false;
+                    cancel(false /* toHome */);
+                }
                 return;
             }
             // At this point, we are accepting the merge.
             t.apply();
             // not using the incoming anim-only surfaces
             info.releaseAnimSurfaces();
-            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
             if (appearedTargets == null) return;
             try {
                 mListener.onTasksAppeared(appearedTargets);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
             }
+            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
         }
 
         /** For now, just set-up a jump-cut to the new activity. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 8e8faca..e8a6a15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -127,7 +127,7 @@
         if (decoration == null) {
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
-            decoration.relayout(taskInfo, startT, finishT);
+            decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
         }
     }
 
@@ -139,7 +139,7 @@
         final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (decoration == null) return;
 
-        decoration.relayout(taskInfo, startT, finishT);
+        decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
     }
 
     @Override
@@ -192,7 +192,8 @@
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
         windowDecoration.setDragPositioningCallback(taskPositioner);
         windowDecoration.setDragDetector(touchEventListener.mDragDetector);
-        windowDecoration.relayout(taskInfo, startT, finishT);
+        windowDecoration.relayout(taskInfo, startT, finishT,
+                false /* applyStartTransactionOnDraw */);
         setupCaptionColor(taskInfo, windowDecoration);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index dfde7e6..116af70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -90,15 +90,15 @@
     @Override
     void relayout(RunningTaskInfo taskInfo) {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        relayout(taskInfo, t, t);
-        mSyncQueue.runInSync(transaction -> {
-            transaction.merge(t);
-            t.close();
-        });
+        // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
+        // synced with the buffer transaction (that draws the View). Both will be shown on screen
+        // at the same, whereas applying them independently causes flickering. See b/270202228.
+        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */);
     }
 
     void relayout(RunningTaskInfo taskInfo,
-            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw) {
         final int shadowRadiusID = taskInfo.isFocused
                 ? R.dimen.freeform_decor_shadow_focused_thickness
                 : R.dimen.freeform_decor_shadow_unfocused_thickness;
@@ -115,6 +115,7 @@
         mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
         mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
+        mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 5226eee..49a5eac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -247,7 +247,7 @@
         if (decoration == null) {
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
-            decoration.relayout(taskInfo, startT, finishT);
+            decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
         }
     }
 
@@ -259,7 +259,7 @@
         final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (decoration == null) return;
 
-        decoration.relayout(taskInfo, startT, finishT);
+        decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
     }
 
     @Override
@@ -335,7 +335,8 @@
         @Override
         public boolean onTouch(View v, MotionEvent e) {
             final int id = v.getId();
-            if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
+            if (id != R.id.caption_handle && id != R.id.desktop_mode_caption
+                    && id != R.id.open_menu_button && id != R.id.close_window) {
                 return false;
             }
             moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
@@ -780,7 +781,8 @@
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
         windowDecoration.setDragPositioningCallback(taskPositioner);
         windowDecoration.setDragDetector(touchEventListener.mDragDetector);
-        windowDecoration.relayout(taskInfo, startT, finishT);
+        windowDecoration.relayout(taskInfo, startT, finishT,
+                false /* applyStartTransactionOnDraw */);
         incrementEventReceiverTasks(taskInfo.displayId);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 67e99d7..af3fb0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -173,15 +173,15 @@
     @Override
     void relayout(ActivityManager.RunningTaskInfo taskInfo) {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        relayout(taskInfo, t, t);
-        mSyncQueue.runInSync(transaction -> {
-            transaction.merge(t);
-            t.close();
-        });
+        // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
+        // synced with the buffer transaction (that draws the View). Both will be shown on screen
+        // at the same, whereas applying them independently causes flickering. See b/270202228.
+        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */);
     }
 
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
-            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw) {
         final int shadowRadiusID = taskInfo.isFocused
                 ? R.dimen.freeform_decor_shadow_focused_thickness
                 : R.dimen.freeform_decor_shadow_unfocused_thickness;
@@ -216,6 +216,7 @@
         mRelayoutParams.mLayoutResId = windowDecorLayoutId;
         mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
+        mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 8b35694..9a1b4ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -238,30 +238,6 @@
         startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
                 .show(mCaptionContainerSurface);
 
-        if (mCaptionWindowManager == null) {
-            // Put caption under a container surface because ViewRootImpl sets the destination frame
-            // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
-            mCaptionWindowManager = new WindowlessWindowManager(
-                    mTaskInfo.getConfiguration(), mCaptionContainerSurface,
-                    null /* hostInputToken */);
-        }
-
-        // Caption view
-        mCaptionWindowManager.setConfiguration(taskConfig);
-        final WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(captionWidth, captionHeight,
-                        WindowManager.LayoutParams.TYPE_APPLICATION,
-                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
-        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
-        lp.setTrustedOverlay();
-        if (mViewHost == null) {
-            mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
-                    mCaptionWindowManager);
-            mViewHost.setView(outResult.mRootView, lp);
-        } else {
-            mViewHost.relayout(lp);
-        }
-
         if (ViewRootImpl.CAPTION_ON_SHELL) {
             outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
 
@@ -287,6 +263,36 @@
                 .show(mTaskSurface);
         finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
                 .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+
+        if (mCaptionWindowManager == null) {
+            // Put caption under a container surface because ViewRootImpl sets the destination frame
+            // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
+            mCaptionWindowManager = new WindowlessWindowManager(
+                    mTaskInfo.getConfiguration(), mCaptionContainerSurface,
+                    null /* hostInputToken */);
+        }
+
+        // Caption view
+        mCaptionWindowManager.setConfiguration(taskConfig);
+        final WindowManager.LayoutParams lp =
+                new WindowManager.LayoutParams(captionWidth, captionHeight,
+                        WindowManager.LayoutParams.TYPE_APPLICATION,
+                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
+        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+        lp.setTrustedOverlay();
+        if (mViewHost == null) {
+            mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
+                    mCaptionWindowManager);
+            if (params.mApplyStartTransactionOnDraw) {
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+            }
+            mViewHost.setView(outResult.mRootView, lp);
+        } else {
+            if (params.mApplyStartTransactionOnDraw) {
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+            }
+            mViewHost.relayout(lp);
+        }
     }
 
     /**
@@ -411,6 +417,8 @@
         int mCaptionX;
         int mCaptionY;
 
+        boolean mApplyStartTransactionOnDraw;
+
         void reset() {
             mLayoutResId = Resources.ID_NULL;
             mCaptionHeightId = Resources.ID_NULL;
@@ -419,6 +427,8 @@
 
             mCaptionX = 0;
             mCaptionY = 0;
+
+            mApplyStartTransactionOnDraw = false;
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 78cfcbd..b67acd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -35,7 +35,9 @@
         captionView.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnTouchListener(onCaptionTouchListener)
         openMenuButton.setOnClickListener(onCaptionButtonClickListener)
+        openMenuButton.setOnTouchListener(onCaptionTouchListener)
         closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+        closeWindowButton.setOnTouchListener(onCaptionTouchListener)
         appNameTextView.text = appName
         appIconImageView.setImageDrawable(appIcon)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 919bf06..4a55429 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -16,8 +16,6 @@
 
 package com.android.wm.shell.bubbles;
 
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
-
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -185,7 +183,10 @@
 
         Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
         appBubbleIntent.setPackage(mContext.getPackageName());
-        mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mock(Icon.class),
+        mAppBubble = Bubble.createAppBubble(
+                appBubbleIntent,
+                new UserHandle(1),
+                mock(Icon.class),
                 mMainExecutor);
 
         mPositioner = new TestableBubblePositioner(mContext,
@@ -1101,14 +1102,15 @@
 
     @Test
     public void test_removeAppBubble_skipsOverflow() {
+        String appBubbleKey = mAppBubble.getKey();
         mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/,
                 false /* showInShade */);
-        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isEqualTo(mAppBubble);
+        assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isEqualTo(mAppBubble);
 
-        mBubbleData.dismissBubbleWithKey(KEY_APP_BUBBLE, Bubbles.DISMISS_USER_GESTURE);
+        mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
 
-        assertThat(mBubbleData.getOverflowBubbleWithKey(KEY_APP_BUBBLE)).isNull();
-        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
+        assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+        assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isNull();
     }
 
     private void verifyUpdateReceived() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index c1e53a9..fc4bfd97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -33,6 +33,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -42,6 +43,7 @@
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
+import android.view.AttachedSurfaceControl;
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
@@ -97,6 +99,8 @@
     @Mock
     private SurfaceControlViewHost mMockSurfaceControlViewHost;
     @Mock
+    private AttachedSurfaceControl mMockRootSurfaceControl;
+    @Mock
     private TestView mMockView;
     @Mock
     private WindowContainerTransaction mMockWindowContainerTransaction;
@@ -129,6 +133,8 @@
 
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
                 .create(any(), any(), any());
+        when(mMockSurfaceControlViewHost.getRootSurfaceControl())
+                .thenReturn(mMockRootSurfaceControl);
     }
 
     @Test
@@ -461,6 +467,43 @@
         verify(mMockSurfaceControlStartT).show(captionContainerSurface);
     }
 
+    @Test
+    public void testRelayout_applyTransactionInSyncWithDraw() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder decorContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(decorContainerSurface);
+        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+        mMockSurfaceControlTransactions.add(t);
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder()
+                        .setBackgroundColor(Color.YELLOW);
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .build();
+        taskInfo.isFocused = true;
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        final SurfaceControl taskSurface = mock(SurfaceControl.class);
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+        windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */);
+
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+    }
+
     private TestWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
         return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
@@ -516,6 +559,12 @@
 
         @Override
         void relayout(ActivityManager.RunningTaskInfo taskInfo) {
+            relayout(taskInfo, false /* applyStartTransactionOnDraw */);
+        }
+
+        void relayout(ActivityManager.RunningTaskInfo taskInfo,
+                boolean applyStartTransactionOnDraw) {
+            mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
             relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
                     mMockWindowContainerTransaction, mMockView, mRelayoutResult);
         }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 70c36a5..34af1f9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -399,7 +399,7 @@
         "libharfbuzz_ng",
         "libimage_io",
         "libjpeg",
-        "libjpegrecoverymap",
+        "libultrahdr",
         "liblog",
         "libminikin",
         "libz",
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 8874ef1..69418b0 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -298,39 +298,39 @@
 }
 ///////////////////////////////////////////////////////////////////////////////
 
-using namespace android::jpegrecoverymap;
+using namespace android::ultrahdr;
 
-jpegr_color_gamut P010Yuv420ToJpegREncoder::findColorGamut(JNIEnv* env, int aDataSpace) {
+ultrahdr_color_gamut P010Yuv420ToJpegREncoder::findColorGamut(JNIEnv* env, int aDataSpace) {
     switch (aDataSpace & ADataSpace::STANDARD_MASK) {
         case ADataSpace::STANDARD_BT709:
-            return jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+            return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
         case ADataSpace::STANDARD_DCI_P3:
-            return jpegr_color_gamut::JPEGR_COLORGAMUT_P3;
+            return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_P3;
         case ADataSpace::STANDARD_BT2020:
-            return jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+            return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
         default:
             jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
             env->ThrowNew(IllegalArgumentException,
                     "The requested color gamut is not supported by JPEG/R.");
     }
 
-    return jpegr_color_gamut::JPEGR_COLORGAMUT_UNSPECIFIED;
+    return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
 }
 
-jpegr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNIEnv* env,
+ultrahdr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNIEnv* env,
         int aDataSpace) {
     switch (aDataSpace & ADataSpace::TRANSFER_MASK) {
         case ADataSpace::TRANSFER_ST2084:
-            return jpegr_transfer_function::JPEGR_TF_PQ;
+            return ultrahdr_transfer_function::ULTRAHDR_TF_PQ;
         case ADataSpace::TRANSFER_HLG:
-            return jpegr_transfer_function::JPEGR_TF_HLG;
+            return ultrahdr_transfer_function::ULTRAHDR_TF_HLG;
         default:
             jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
             env->ThrowNew(IllegalArgumentException,
                     "The requested HDR transfer function is not supported by JPEG/R.");
     }
 
-    return jpegr_transfer_function::JPEGR_TF_UNSPECIFIED;
+    return ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED;
 }
 
 bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
@@ -344,13 +344,13 @@
         return false;
     }
 
-    jpegr_color_gamut hdrColorGamut = findColorGamut(env, hdrColorSpace);
-    jpegr_color_gamut sdrColorGamut = findColorGamut(env, sdrColorSpace);
-    jpegr_transfer_function hdrTransferFunction = findHdrTransferFunction(env, hdrColorSpace);
+    ultrahdr_color_gamut hdrColorGamut = findColorGamut(env, hdrColorSpace);
+    ultrahdr_color_gamut sdrColorGamut = findColorGamut(env, sdrColorSpace);
+    ultrahdr_transfer_function hdrTransferFunction = findHdrTransferFunction(env, hdrColorSpace);
 
-    if (hdrColorGamut == jpegr_color_gamut::JPEGR_COLORGAMUT_UNSPECIFIED
-            || sdrColorGamut == jpegr_color_gamut::JPEGR_COLORGAMUT_UNSPECIFIED
-            || hdrTransferFunction == jpegr_transfer_function::JPEGR_TF_UNSPECIFIED) {
+    if (hdrColorGamut == ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED
+            || sdrColorGamut == ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED
+            || hdrTransferFunction == ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED) {
         return false;
     }
 
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
index d22a26c..8ef7805 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.h
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -2,7 +2,7 @@
 #define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
 
 #include <android/data_space.h>
-#include <jpegrecoverymap/jpegr.h>
+#include <ultrahdr/jpegr.h>
 
 extern "C" {
     #include "jpeglib.h"
@@ -103,7 +103,7 @@
      *  @param aDataSpace data space defined in data_space.h.
      *  @return color gamut for JPEG/R.
      */
-    static android::jpegrecoverymap::jpegr_color_gamut findColorGamut(JNIEnv* env, int aDataSpace);
+    static android::ultrahdr::ultrahdr_color_gamut findColorGamut(JNIEnv* env, int aDataSpace);
 
     /** Map data space (defined in DataSpace.java and data_space.h) to the transfer function
      *  used in JPEG/R
@@ -112,7 +112,7 @@
      *  @param aDataSpace data space defined in data_space.h.
      *  @return color gamut for JPEG/R.
      */
-    static android::jpegrecoverymap::jpegr_transfer_function findHdrTransferFunction(
+    static android::ultrahdr::ultrahdr_transfer_function findHdrTransferFunction(
             JNIEnv* env, int aDataSpace);
 };
 
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 1a47db5..da4f66d 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -293,8 +293,10 @@
                 // with the same canvas transformation + clip into the target
                 // canvas then draw the layer on top
                 if (renderNode->hasHolePunches()) {
+                    canvas->save();
                     TransformCanvas transformCanvas(canvas, SkBlendMode::kDstOut);
                     displayList->draw(&transformCanvas);
+                    canvas->restore();
                 }
                 canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
                                       SkRect::Make(dstBounds), sampling, &paint,
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3123ee6..e73cf87 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1237,9 +1237,6 @@
     public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
     /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
-    /** @hide */
-    public static final Set<Integer> DEVICE_IN_ALL_BLE_SET;
-
     static {
         DEVICE_IN_ALL_SET = new HashSet<>();
         DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION);
@@ -1279,66 +1276,6 @@
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY);
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE);
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
-
-        DEVICE_IN_ALL_BLE_SET = new HashSet<>();
-        DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothDevice(int deviceType) {
-        return isBluetoothA2dpOutDevice(deviceType)
-                || isBluetoothScoDevice(deviceType)
-                || isBluetoothLeDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothOutDevice(int deviceType) {
-        return isBluetoothA2dpOutDevice(deviceType)
-                || isBluetoothScoOutDevice(deviceType)
-                || isBluetoothLeOutDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothInDevice(int deviceType) {
-        return isBluetoothScoInDevice(deviceType)
-                || isBluetoothLeInDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothA2dpOutDevice(int deviceType) {
-        return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothScoOutDevice(int deviceType) {
-        return DEVICE_OUT_ALL_SCO_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothScoInDevice(int deviceType) {
-        return DEVICE_IN_ALL_SCO_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothScoDevice(int deviceType) {
-        return isBluetoothScoOutDevice(deviceType)
-                || isBluetoothScoInDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothLeOutDevice(int deviceType) {
-        return DEVICE_OUT_ALL_BLE_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothLeInDevice(int deviceType) {
-        return DEVICE_IN_ALL_BLE_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothLeDevice(int deviceType) {
-        return isBluetoothLeOutDevice(deviceType)
-                || isBluetoothLeInDevice(deviceType);
     }
 
     /** @hide */
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 7e1bbe3..29e8716 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -293,12 +293,16 @@
      * Set the component name of the manifest-declared {@link android.content.BroadcastReceiver}
      * class that should receive media buttons. This allows restarting playback after the session
      * has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON}
-     * intent will be sent to the broadcast receiver.
-     * <p>
-     * Note: The given {@link android.content.BroadcastReceiver} should belong to the same package
-     * as the context that was given when creating {@link MediaSession}.
+     * intent will be sent to the broadcast receiver. On apps targeting Android U and above, this
+     * will throw an {@link IllegalArgumentException} if the provided {@link ComponentName} does not
+     * resolve to an existing {@link android.content.BroadcastReceiver broadcast receiver}.
+     *
+     * <p>Note: The given {@link android.content.BroadcastReceiver} should belong to the same
+     * package as the context that was given when creating {@link MediaSession}.
      *
      * @param broadcastReceiver the component name of the BroadcastReceiver class
+     * @throws IllegalArgumentException if {@code broadcastReceiver} does not exist on apps
+     *     targeting Android U and above
      */
     public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) {
         try {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 7581b5c..8b9c8b9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -40,9 +40,7 @@
 import com.android.credentialmanager.createflow.CreateCredentialScreen
 import com.android.credentialmanager.createflow.hasContentToDisplay
 import com.android.credentialmanager.getflow.GetCredentialScreen
-import com.android.credentialmanager.getflow.GetGenericCredentialScreen
 import com.android.credentialmanager.getflow.hasContentToDisplay
-import com.android.credentialmanager.getflow.isFallbackScreen
 import com.android.credentialmanager.ui.theme.PlatformTheme
 
 @ExperimentalMaterialApi
@@ -161,19 +159,11 @@
                 providerActivityLauncher = launcher
             )
         } else if (getCredentialUiState != null && hasContentToDisplay(getCredentialUiState)) {
-            if (isFallbackScreen(getCredentialUiState)) {
-                GetGenericCredentialScreen(
-                    viewModel = viewModel,
-                    getCredentialUiState = getCredentialUiState,
-                    providerActivityLauncher = launcher
-                )
-            } else {
-                GetCredentialScreen(
-                    viewModel = viewModel,
-                    getCredentialUiState = getCredentialUiState,
-                    providerActivityLauncher = launcher
-                )
-            }
+            GetCredentialScreen(
+                viewModel = viewModel,
+                getCredentialUiState = getCredentialUiState,
+                providerActivityLauncher = launcher
+            )
         } else {
             Log.d(Constants.LOG_TAG, "UI wasn't able to render neither get nor create flow")
             reportInstantiationErrorAndFinishActivity(credManRepo)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index f08bbf4..57035d4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -50,9 +50,7 @@
 import androidx.credentials.CreateCredentialRequest
 import androidx.credentials.CreateCustomCredentialRequest
 import androidx.credentials.CreatePasswordRequest
-import androidx.credentials.CredentialOption
 import androidx.credentials.CreatePublicKeyCredentialRequest
-import androidx.credentials.GetPublicKeyCredentialOption
 import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
 import androidx.credentials.provider.Action
 import androidx.credentials.provider.AuthenticationAction
@@ -194,20 +192,8 @@
             originName: String?,
         ): com.android.credentialmanager.getflow.RequestDisplayInfo? {
             val getCredentialRequest = requestInfo?.getCredentialRequest ?: return null
-            val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any {
-                val credentialOptionJetpack = CredentialOption.createFrom(
-                    it.type,
-                    it.credentialRetrievalData,
-                    it.credentialRetrievalData,
-                    it.isSystemProviderRequired,
-                    it.allowedProviders,
-                )
-                if (credentialOptionJetpack is GetPublicKeyCredentialOption) {
-                    credentialOptionJetpack.preferImmediatelyAvailableCredentials
-                } else {
-                    false
-                }
-            }
+            val preferImmediatelyAvailableCredentials = getCredentialRequest.data.getBoolean(
+                "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS")
             val preferUiBrandingComponentName =
                 getCredentialRequest.data.getParcelable(
                     "androidx.credentials.BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME",
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 516c1a3..74933c9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 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.
@@ -200,18 +200,31 @@
             authenticationEntryList.isEmpty()) || (sortedUserNameToCredentialEntryList.isEmpty() &&
             authenticationEntryList.size == 1)
         item {
-            HeadlineText(
-                text = stringResource(
-                    if (hasSingleEntry) {
-                        if (sortedUserNameToCredentialEntryList.firstOrNull()
-                                ?.sortedCredentialEntryList?.first()?.credentialType
-                            == CredentialType.PASSKEY
-                        ) R.string.get_dialog_title_use_passkey_for
-                        else R.string.get_dialog_title_use_sign_in_for
-                    } else R.string.get_dialog_title_choose_sign_in_for,
-                    requestDisplayInfo.appName
-                ),
-            )
+            if (requestDisplayInfo.preferIdentityDocUi) {
+                HeadlineText(
+                    text = stringResource(
+                        if (hasSingleEntry) {
+                            R.string.get_dialog_title_use_info_on
+                        } else {
+                            R.string.get_dialog_title_choose_option_for
+                        },
+                        requestDisplayInfo.appName
+                    ),
+                )
+            } else {
+                HeadlineText(
+                    text = stringResource(
+                        if (hasSingleEntry) {
+                            if (sortedUserNameToCredentialEntryList.firstOrNull()
+                                    ?.sortedCredentialEntryList?.first()?.credentialType
+                                == CredentialType.PASSKEY
+                            ) R.string.get_dialog_title_use_passkey_for
+                            else R.string.get_dialog_title_use_sign_in_for
+                        } else R.string.get_dialog_title_choose_sign_in_for,
+                        requestDisplayInfo.appName
+                    ),
+                )
+            }
         }
         item { Divider(thickness = 24.dp, color = Color.Transparent) }
         item {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
deleted file mode 100644
index 57fefbe..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.getflow
-
-import androidx.activity.compose.ManagedActivityResultLauncher
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.IntentSenderRequest
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material3.Divider
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.asImageBitmap
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.rememberSystemUiController
-import com.android.credentialmanager.CredentialSelectorViewModel
-import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.ProviderActivityState
-import com.android.credentialmanager.common.ui.ConfirmButton
-import com.android.credentialmanager.common.ui.CredentialContainerCard
-import com.android.credentialmanager.common.ui.CtaButtonRow
-import com.android.credentialmanager.common.ui.HeadlineIcon
-import com.android.credentialmanager.common.ui.HeadlineText
-import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
-import com.android.credentialmanager.common.ui.ModalBottomSheet
-import com.android.credentialmanager.common.ui.SheetContainerCard
-import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor
-import com.android.credentialmanager.logging.GetCredentialEvent
-import com.android.internal.logging.UiEventLogger
-
-
-@Composable
-fun GetGenericCredentialScreen(
-        viewModel: CredentialSelectorViewModel,
-        getCredentialUiState: GetCredentialUiState,
-        providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
-) {
-    val sysUiController = rememberSystemUiController()
-    setBottomSheetSystemBarsColor(sysUiController)
-    ModalBottomSheet(
-        sheetContent = {
-            when (viewModel.uiState.providerActivityState) {
-                ProviderActivityState.NOT_APPLICABLE -> {
-                    PrimarySelectionCardGeneric(
-                            requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
-                            providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
-                            providerInfoList = getCredentialUiState.providerInfoList,
-                            onEntrySelected = viewModel::getFlowOnEntrySelected,
-                            onConfirm = viewModel::getFlowOnConfirmEntrySelected,
-                            onLog = { viewModel.logUiEvent(it) },
-                    )
-                    viewModel.uiMetrics.log(GetCredentialEvent
-                            .CREDMAN_GET_CRED_SCREEN_PRIMARY_SELECTION)
-                }
-                ProviderActivityState.READY_TO_LAUNCH -> {
-                    // Launch only once per providerActivityState change so that the provider
-                    // UI will not be accidentally launched twice.
-                    LaunchedEffect(viewModel.uiState.providerActivityState) {
-                        viewModel.launchProviderUi(providerActivityLauncher)
-                    }
-                    viewModel.uiMetrics.log(GetCredentialEvent
-                            .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_READY_TO_LAUNCH)
-                }
-                ProviderActivityState.PENDING -> {
-                    // Hide our content when the provider activity is active.
-                    viewModel.uiMetrics.log(GetCredentialEvent
-                            .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_PENDING)
-                }
-            }
-        },
-        onDismiss = viewModel::onUserCancel,
-    )
-}
-
-@Composable
-fun PrimarySelectionCardGeneric(
-        requestDisplayInfo: RequestDisplayInfo,
-        providerDisplayInfo: ProviderDisplayInfo,
-        providerInfoList: List<ProviderInfo>,
-        onEntrySelected: (BaseEntry) -> Unit,
-        onConfirm: () -> Unit,
-        onLog: @Composable (UiEventLogger.UiEventEnum) -> Unit,
-) {
-    val sortedUserNameToCredentialEntryList =
-            providerDisplayInfo.sortedUserNameToCredentialEntryList
-    val totalEntriesCount = sortedUserNameToCredentialEntryList
-            .flatMap { it.sortedCredentialEntryList }.size
-    SheetContainerCard {
-        // When only one provider (not counting the remote-only provider) exists, display that
-        // provider's icon + name up top.
-        if (providerInfoList.size <= 2) { // It's only possible to be the single provider case
-            // if we are started with no more than 2 providers.
-            val nonRemoteProviderList = providerInfoList.filter(
-                { it.credentialEntryList.isNotEmpty() || it.authenticationEntryList.isNotEmpty() }
-            )
-            if (nonRemoteProviderList.size == 1) {
-                val providerInfo = nonRemoteProviderList.firstOrNull() // First should always work
-                // but just to be safe.
-                if (providerInfo != null) {
-                    item {
-                        HeadlineIcon(
-                                bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
-                                tint = Color.Unspecified,
-                        )
-                    }
-                    item { Divider(thickness = 4.dp, color = Color.Transparent) }
-                    item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) }
-                    item { Divider(thickness = 16.dp, color = Color.Transparent) }
-                }
-            }
-        }
-
-        item {
-            HeadlineText(
-                    text = stringResource(
-                            if (totalEntriesCount == 1) {
-                                R.string.get_dialog_title_use_info_on
-                            } else {
-                                R.string.get_dialog_title_choose_option_for
-                            },
-                            requestDisplayInfo.appName
-                    ),
-            )
-        }
-        item { Divider(thickness = 24.dp, color = Color.Transparent) }
-        item {
-            CredentialContainerCard {
-                Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
-                    sortedUserNameToCredentialEntryList.forEach {
-                        // TODO(b/275375861): fallback UI merges entries by account names.
-                        //  Need a strategy to be able to show all entries.
-                        CredentialEntryRow(
-                                credentialEntryInfo = it.sortedCredentialEntryList.first(),
-                                onEntrySelected = onEntrySelected,
-                                enforceOneLine = true,
-                        )
-                    }
-                }
-            }
-        }
-        item { Divider(thickness = 24.dp, color = Color.Transparent) }
-        item {
-            if (totalEntriesCount == 1) {
-                CtaButtonRow(
-                    rightButton = {
-                        ConfirmButton(
-                            stringResource(R.string.get_dialog_button_label_continue),
-                            onClick = onConfirm
-                        )
-                    }
-                )
-            }
-        }
-    }
-    onLog(GetCredentialEvent.CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD)
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index a4a163b..716f474 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -41,10 +41,6 @@
             !state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
 }
 
-internal fun isFallbackScreen(state: GetCredentialUiState): Boolean {
-    return state.requestDisplayInfo.preferIdentityDocUi
-}
-
 internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): CredentialEntryInfo? {
     if (providerDisplayInfo.authenticationEntryList.isNotEmpty()) {
         return null
diff --git a/packages/SettingsProvider/src/android/provider/settings/OWNERS b/packages/SettingsProvider/src/android/provider/settings/OWNERS
index 0f88811..d901e2c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/OWNERS
+++ b/packages/SettingsProvider/src/android/provider/settings/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 656484
 
-include platform/frameworks/base:/services/backup/OWNERS
+include platform/frameworks/base:/services/backup/BACKUP_OWNERS
 
diff --git a/packages/SettingsProvider/test/src/android/provider/OWNERS b/packages/SettingsProvider/test/src/android/provider/OWNERS
index 0f88811..db4b27c 100644
--- a/packages/SettingsProvider/test/src/android/provider/OWNERS
+++ b/packages/SettingsProvider/test/src/android/provider/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 656484
 
-include platform/frameworks/base:/services/backup/OWNERS
-
+include platform/frameworks/base:/services/backup/BACKUP_OWNERS
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index a202e16..706666c 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -449,6 +449,7 @@
                     Settings.Global.SHOW_PEOPLE_SPACE,
                     Settings.Global.SHOW_NEW_NOTIF_DISMISS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
+                    Settings.Global.SHOW_TARE_DEVELOPER_OPTIONS,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
                     Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
                     Settings.Global.SIGNED_CONFIG_VERSION,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 78d93bd..751fbaa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -634,6 +634,9 @@
     <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
     <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
 
+    <!-- Permission required for CTS test - CtsVoiceInteractionTestCases -->
+    <uses-permission android:name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/>
+
     <uses-permission android:name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE" />
 
     <!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 3eb7fd8..23f16f2 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -18,6 +18,7 @@
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.fonts.Font
+import android.graphics.fonts.FontVariationAxis
 import android.graphics.text.PositionedGlyphs
 import android.text.Layout
 import android.text.TextPaint
@@ -211,8 +212,15 @@
                     run.baseX[i] = MathUtils.lerp(run.baseX[i], run.targetX[i], progress)
                     run.baseY[i] = MathUtils.lerp(run.baseY[i], run.targetY[i], progress)
                 }
-                run.fontRuns.forEach {
-                    it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress)
+                run.fontRuns.forEach { fontRun ->
+                    fontRun.baseFont =
+                        fontInterpolator.lerp(fontRun.baseFont, fontRun.targetFont, progress)
+                    val tmpFontVariationsArray = mutableListOf<FontVariationAxis>()
+                    fontRun.baseFont.axes.forEach {
+                        tmpFontVariationsArray.add(FontVariationAxis(it.tag, it.styleValue))
+                    }
+                    basePaint.fontVariationSettings =
+                        FontVariationAxis.toFontVariationSettings(tmpFontVariationsArray)
                 }
             }
         }
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
index a5f832a..ff150c8c 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
@@ -38,7 +38,7 @@
     }
 
     @Test
-    fun `No violations`() {
+    fun noViolations() {
         lint()
             .files(
                 *LEGITIMATE_FILES,
@@ -51,7 +51,7 @@
     }
 
     @Test
-    fun `Violation - domain depends on ui`() {
+    fun violation_domainDependsOnUi() {
         lint()
             .files(
                 *LEGITIMATE_FILES,
@@ -86,7 +86,7 @@
     }
 
     @Test
-    fun `Violation - ui depends on data`() {
+    fun violation_uiDependsOnData() {
         lint()
             .files(
                 *LEGITIMATE_FILES,
@@ -121,7 +121,7 @@
     }
 
     @Test
-    fun `Violation - shared depends on all other layers`() {
+    fun violation_sharedDependsOnAllOtherLayers() {
         lint()
             .files(
                 *LEGITIMATE_FILES,
@@ -166,7 +166,7 @@
     }
 
     @Test
-    fun `Violation - data depends on domain`() {
+    fun violation_dataDependsOnDomain() {
         lint()
             .files(
                 *LEGITIMATE_FILES,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index eaf3229..34adcc7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -32,6 +32,12 @@
 import com.android.systemui.plugins.PluginLifecycleManager
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.plugins.log.LogMessage
+import com.android.systemui.plugins.log.LogMessageImpl
+import com.android.systemui.plugins.log.MessageInitializer
+import com.android.systemui.plugins.log.MessagePrinter
 import com.android.systemui.util.Assert
 import java.io.PrintWriter
 import java.util.concurrent.ConcurrentHashMap
@@ -40,7 +46,6 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
-private const val DEBUG = true
 private val KEY_TIMESTAMP = "appliedTimestamp"
 
 private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
@@ -55,6 +60,32 @@
     return result ?: value
 }
 
+private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
+
+private inline fun LogBuffer?.tryLog(
+    tag: String,
+    level: LogLevel,
+    messageInitializer: MessageInitializer,
+    noinline messagePrinter: MessagePrinter,
+    ex: Throwable? = null,
+) {
+    if (this != null) {
+        // Wrap messagePrinter to convert it from crossinline to noinline
+        this.log(tag, level, messageInitializer, messagePrinter, ex)
+    } else {
+        messageInitializer(TMP_MESSAGE)
+        val msg = messagePrinter(TMP_MESSAGE)
+        when (level) {
+            LogLevel.VERBOSE -> Log.v(tag, msg, ex)
+            LogLevel.DEBUG -> Log.d(tag, msg, ex)
+            LogLevel.INFO -> Log.i(tag, msg, ex)
+            LogLevel.WARNING -> Log.w(tag, msg, ex)
+            LogLevel.ERROR -> Log.e(tag, msg, ex)
+            LogLevel.WTF -> Log.wtf(tag, msg, ex)
+        }
+    }
+}
+
 /** ClockRegistry aggregates providers and plugins */
 open class ClockRegistry(
     val context: Context,
@@ -66,8 +97,9 @@
     val handleAllUsers: Boolean,
     defaultClockProvider: ClockProvider,
     val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
+    val logBuffer: LogBuffer? = null,
     val keepAllLoaded: Boolean,
-    val subTag: String,
+    subTag: String,
 ) {
     private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
     interface ClockChangeListener {
@@ -113,9 +145,11 @@
                         }
 
                     if (manager != info.manager) {
-                        Log.e(
+                        logBuffer.tryLog(
                             TAG,
-                            "Clock Id conflict on load: $id is registered to another provider"
+                            LogLevel.ERROR,
+                            { str1 = id },
+                            { "Clock Id conflict on load: $str1 is double registered" }
                         )
                         continue
                     }
@@ -138,9 +172,11 @@
                     val id = clock.clockId
                     val info = availableClocks[id]
                     if (info?.manager != manager) {
-                        Log.e(
+                        logBuffer.tryLog(
                             TAG,
-                            "Clock Id conflict on unload: $id is registered to another provider"
+                            LogLevel.ERROR,
+                            { str1 = id },
+                            { "Clock Id conflict on unload: $str1 is double registered" }
                         )
                         continue
                     }
@@ -211,7 +247,7 @@
 
                 ClockSettings.deserialize(json)
             } catch (ex: Exception) {
-                Log.e(TAG, "Failed to parse clock settings", ex)
+                logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
                 null
             }
         settings = result
@@ -240,7 +276,7 @@
                 )
             }
         } catch (ex: Exception) {
-            Log.e(TAG, "Failed to set clock settings", ex)
+            logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
         }
         settings = value
     }
@@ -400,46 +436,55 @@
     }
 
     private fun onConnected(clockId: ClockId) {
-        if (DEBUG) {
-            Log.i(TAG, "Connected $clockId")
-        }
-
+        logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
         if (currentClockId == clockId) {
-            if (DEBUG) {
-                Log.i(TAG, "Current clock ($clockId) was connected")
-            }
+            logBuffer.tryLog(
+                TAG,
+                LogLevel.INFO,
+                { str1 = clockId },
+                { "Current clock ($str1) was connected" }
+            )
         }
     }
 
     private fun onLoaded(clockId: ClockId) {
-        if (DEBUG) {
-            Log.i(TAG, "Loaded $clockId")
-        }
+        logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
 
         if (currentClockId == clockId) {
-            Log.i(TAG, "Current clock ($clockId) was loaded")
+            logBuffer.tryLog(
+                TAG,
+                LogLevel.INFO,
+                { str1 = clockId },
+                { "Current clock ($str1) was loaded" }
+            )
             triggerOnCurrentClockChanged()
         }
     }
 
     private fun onUnloaded(clockId: ClockId) {
-        if (DEBUG) {
-            Log.i(TAG, "Unloaded $clockId")
-        }
+        logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
 
         if (currentClockId == clockId) {
-            Log.w(TAG, "Current clock ($clockId) was unloaded")
+            logBuffer.tryLog(
+                TAG,
+                LogLevel.WARNING,
+                { str1 = clockId },
+                { "Current clock ($str1) was unloaded" }
+            )
             triggerOnCurrentClockChanged()
         }
     }
 
     private fun onDisconnected(clockId: ClockId) {
-        if (DEBUG) {
-            Log.i(TAG, "Disconnected $clockId")
-        }
+        logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
 
         if (currentClockId == clockId) {
-            Log.w(TAG, "Current clock ($clockId) was disconnected")
+            logBuffer.tryLog(
+                TAG,
+                LogLevel.WARNING,
+                { str1 = clockId },
+                { "Current clock ($str1) was disconnected" }
+            )
         }
     }
 
@@ -466,12 +511,27 @@
         if (isEnabled && clockId.isNotEmpty()) {
             val clock = createClock(clockId)
             if (clock != null) {
-                if (DEBUG) {
-                    Log.i(TAG, "Rendering clock $clockId")
-                }
+                logBuffer.tryLog(
+                    TAG,
+                    LogLevel.INFO,
+                    { str1 = clockId },
+                    { "Rendering clock $str1" }
+                )
                 return clock
+            } else if (availableClocks.containsKey(clockId)) {
+                logBuffer.tryLog(
+                    TAG,
+                    LogLevel.WARNING,
+                    { str1 = clockId },
+                    { "Clock $str1 not loaded; using default" }
+                )
             } else {
-                Log.e(TAG, "Clock $clockId not found; using default")
+                logBuffer.tryLog(
+                    TAG,
+                    LogLevel.ERROR,
+                    { str1 = clockId },
+                    { "Clock $str1 not found; using default" }
+                )
             }
         }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 3ec3b5c..6ca7f12 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -62,10 +62,7 @@
     private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
 
     override val events: DefaultClockEvents
-    override lateinit var animations: DefaultClockAnimations
-        private set
-
-    override val config = ClockConfig(hasCustomPositionUpdatedAnimation = true)
+    override val config = ClockConfig()
 
     init {
         val parent = FrameLayout(ctx)
@@ -84,13 +81,13 @@
         clocks = listOf(smallClock.view, largeClock.view)
 
         events = DefaultClockEvents()
-        animations = DefaultClockAnimations(0f, 0f)
         events.onLocaleChanged(Locale.getDefault())
     }
 
     override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
         largeClock.recomputePadding(null)
-        animations = DefaultClockAnimations(dozeFraction, foldFraction)
+        largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction)
+        smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction)
         events.onColorPaletteChanged(resources)
         events.onTimeZoneChanged(TimeZone.getDefault())
         smallClock.events.onTimeTick()
@@ -115,6 +112,9 @@
                 view.logBuffer = value
             }
 
+        override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
+            internal set
+
         init {
             if (seedColor != null) {
                 currentColor = seedColor!!
@@ -170,6 +170,12 @@
         view: AnimatableClockView,
         seedColor: Int?,
     ) : DefaultClockFaceController(view, seedColor) {
+        override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
+
+        init {
+            animations = LargeClockAnimations(view, 0f, 0f)
+        }
+
         override fun recomputePadding(targetRegion: Rect?) {
             // We center the view within the targetRegion instead of within the parent
             // view by computing the difference and adding that to the padding.
@@ -220,7 +226,8 @@
         }
     }
 
-    inner class DefaultClockAnimations(
+    open inner class DefaultClockAnimations(
+        val view: AnimatableClockView,
         dozeFraction: Float,
         foldFraction: Float,
     ) : ClockAnimations {
@@ -229,34 +236,40 @@
 
         init {
             if (foldState.isActive) {
-                clocks.forEach { it.animateFoldAppear(false) }
+                view.animateFoldAppear(false)
             } else {
-                clocks.forEach { it.animateDoze(dozeState.isActive, false) }
+                view.animateDoze(dozeState.isActive, false)
             }
         }
 
         override fun enter() {
             if (!dozeState.isActive) {
-                clocks.forEach { it.animateAppearOnLockscreen() }
+                view.animateAppearOnLockscreen()
             }
         }
 
-        override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
+        override fun charge() = view.animateCharge { dozeState.isActive }
 
         override fun fold(fraction: Float) {
             val (hasChanged, hasJumped) = foldState.update(fraction)
             if (hasChanged) {
-                clocks.forEach { it.animateFoldAppear(!hasJumped) }
+                view.animateFoldAppear(!hasJumped)
             }
         }
 
         override fun doze(fraction: Float) {
             val (hasChanged, hasJumped) = dozeState.update(fraction)
             if (hasChanged) {
-                clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
+                view.animateDoze(dozeState.isActive, !hasJumped)
             }
         }
+    }
 
+    inner class LargeClockAnimations(
+        view: AnimatableClockView,
+        dozeFraction: Float,
+        foldFraction: Float,
+    ) : DefaultClockAnimations(view, dozeFraction, foldFraction) {
         override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {
             largeClock.moveForSplitShade(fromRect, toRect, fraction)
         }
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index 9b4c21e..4a6240b 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -108,20 +108,13 @@
 
 ### Using injection with Fragments
 
-Fragments are created as part of the FragmentManager, so they need to be
-setup so the manager knows how to create them. To do that, add a method
-to com.android.systemui.fragments.FragmentService$FragmentCreator that
-returns your fragment class. That is all that is required, once the method
-exists, FragmentService will automatically pick it up and use injection
-whenever your fragment needs to be created.
+Fragments are created as part of the FragmentManager, so injectable Fragments need to be registered
+so the manager knows how to create them. This is done via
+[FragmentService#addFragmentInstantiationProvider](../src/com/android/systemui/fragments/FragmentService.java).
+Pass it the class of your fragment and a `Provider` for your fragment at some time before your
+Fragment is accessed.
 
-```java
-public interface FragmentCreator {
-    NavigationBarFragment createNavigationBar();
-}
-```
-
-If you need to create your fragment (i.e. for the add or replace transaction),
+When you need to create your fragment (i.e. for the add or replace transaction),
 then the FragmentHostManager can do this for you.
 
 ```java
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 05630e7..8ef2d80 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -69,9 +69,6 @@
     /** Events that clocks may need to respond to */
     val events: ClockEvents
 
-    /** Triggers for various animations */
-    val animations: ClockAnimations
-
     /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
     fun initialize(
         resources: Resources,
@@ -79,8 +76,10 @@
         foldFraction: Float,
     ) {
         events.onColorPaletteChanged(resources)
-        animations.doze(dozeFraction)
-        animations.fold(foldFraction)
+        smallClock.animations.doze(dozeFraction)
+        largeClock.animations.doze(dozeFraction)
+        smallClock.animations.fold(foldFraction)
+        largeClock.animations.fold(foldFraction)
         smallClock.events.onTimeTick()
         largeClock.events.onTimeTick()
     }
@@ -100,6 +99,9 @@
     /** Events specific to this clock face */
     val events: ClockFaceEvents
 
+    /** Triggers for various animations */
+    val animations: ClockAnimations
+
     /** Some clocks may log debug information */
     var logBuffer: LogBuffer?
 }
@@ -192,13 +194,6 @@
 
 /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
 data class ClockConfig(
-    /**
-     * Whether this clock has a custom position update animation. If true, the keyguard will call
-     * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
-     * animation will be used (e.g. a simple translation).
-     */
-    val hasCustomPositionUpdatedAnimation: Boolean = false,
-
     /** Transition to AOD should move smartspace like large clock instead of small clock */
     val useAlternateSmartspaceAODTransition: Boolean = false,
 
@@ -213,6 +208,13 @@
 
     /** Call to check whether the clock consumes weather data */
     val hasCustomWeatherDataDisplay: Boolean = false,
+
+    /**
+     * Whether this clock has a custom position update animation. If true, the keyguard will call
+     * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
+     * animation will be used (e.g. a simple translation).
+     */
+    val hasCustomPositionUpdatedAnimation: Boolean = false,
 )
 
 /** Structure for keeping clock-specific settings */
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 10bb00c..a8ed843 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -1,156 +1,13 @@
-# Preserve line number information for debugging stack traces.
--keepattributes SourceFile,LineNumberTable
+-include proguard_common.flags
 
-# Preserve relationship information that can impact simple class naming.
--keepattributes EnclosingMethod,InnerClasses
-
--keep class com.android.systemui.recents.OverviewProxyRecentsImpl
--keep class com.android.systemui.statusbar.car.CarStatusBar
--keep class com.android.systemui.statusbar.phone.CentralSurfaces
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
--keep class ** extends com.android.systemui.SystemUIInitializer {
-    *;
-}
--keep class * extends com.android.systemui.CoreStartable
--keep class * implements com.android.systemui.CoreStartable$Injector
-
-# Needed for builds to properly initialize KeyFrames from xml scene
--keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
-  public <init>();
-}
-
-# Needed to ensure callback field references are kept in their respective
-# owning classes when the downstream callback registrars only store weak refs.
-# TODO(b/264686688): Handle these cases with more targeted annotations.
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  private com.android.keyguard.KeyguardUpdateMonitorCallback *;
-  private com.android.systemui.privacy.PrivacyConfig$Callback *;
-  private com.android.systemui.privacy.PrivacyItemController$Callback *;
-  private com.android.systemui.settings.UserTracker$Callback *;
-  private com.android.systemui.statusbar.phone.StatusBarWindowCallback *;
-  private com.android.systemui.util.service.Observer$Callback *;
-  private com.android.systemui.util.service.ObservableServiceConnection$Callback *;
-}
-# Note that these rules are temporary companions to the above rules, required
-# for cases like Kotlin where fields with anonymous types use the anonymous type
-# rather than the supertype.
--if class * extends com.android.keyguard.KeyguardUpdateMonitorCallback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
--if class * extends com.android.systemui.privacy.PrivacyConfig$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
--if class * extends com.android.systemui.privacy.PrivacyItemController$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
--if class * extends com.android.systemui.settings.UserTracker$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
--if class * extends com.android.systemui.statusbar.phone.StatusBarWindowCallback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
--if class * extends com.android.systemui.util.service.Observer$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
--if class * extends com.android.systemui.util.service.ObservableServiceConnection$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
-  <1> *;
-}
-
--keepclasseswithmembers class * {
-    public <init>(android.content.Context, android.util.AttributeSet);
-}
-
--keep class ** extends androidx.preference.PreferenceFragment
--keep class com.android.systemui.tuner.*
-
-# The plugins subpackage acts as a shared library that might be referenced in
-# dynamically-loaded plugin APKs.
--keep class com.android.systemui.plugins.** {
-    *;
-}
--keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
-    *;
-}
--keep class androidx.core.app.CoreComponentFactory
-
--keep public class * extends com.android.systemui.CoreStartable {
-    public <init>(android.content.Context);
-}
-
-# Keep the wm shell lib
--keep class com.android.wm.shell.*
-# Keep the protolog group methods that are called by the generated code
--keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
+-keep class com.android.systemui.SystemUIInitializerImpl {
     *;
 }
 
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent { !synthetic *; }
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { !synthetic *; }
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.Dagger** { !synthetic *; }
--keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.Dagger** { !synthetic *; }
-
-# Prevent optimization or access modification of any referenced code that may
-# conflict with code in the bootclasspath.
-# TODO(b/222468116): Resolve such collisions in the build system.
--keepnames class android.**.nano.** { *; }
--keepnames class com.android.**.nano.** { *; }
--keepnames class com.android.internal.protolog.** { *; }
--keepnames class android.hardware.common.** { *; }
-
-# Allows proguard to make private and protected methods and fields public as
-# part of optimization. This lets proguard inline trivial getter/setter methods.
--allowaccessmodification
-
-# Removes runtime checks added through Kotlin to JVM code genereration to
-# avoid linear growth as more Kotlin code is converted / added to the codebase.
-# These checks are generally applied to Java platform types (values returned
-# from Java code that don't have nullness annotations), but we remove them to
-# avoid code size increases.
-#
-# See also https://kotlinlang.org/docs/reference/java-interop.html
-#
-# TODO(b/199941987): Consider standardizing these rules in a central place as
-# Kotlin gains adoption with other platform targets.
--assumenosideeffects class kotlin.jvm.internal.Intrinsics {
-    # Remove check for method parameters being null
-    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
-
-    # When a Java platform type is returned and passed to Kotlin NonNull method,
-    # remove the null check
-    static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
-    static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
-
-    # Remove check that final value returned from method is null, if passing
-    # back Java platform type.
-    static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
-    static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
-
-    # Null check for accessing a field from a parent class written in Java.
-    static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
-    static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
-
-    # Removes code generated from !! operator which converts Nullable type to
-    # NonNull type. These would throw an NPE immediate after on access.
-    static void checkNotNull(java.lang.Object, java.lang.String);
-    static void checkNotNullParameter(java.lang.Object, java.lang.String);
-
-    # Removes lateinit var check being used before being set. Check is applied
-    # on every field access without this.
-    static void throwUninitializedPropertyAccessException(java.lang.String);
+-keep class com.android.systemui.tv.TvSystemUIInitializer {
+    *;
 }
-# Strip verbose logs.
--assumenosideeffects class android.util.Log {
-  static *** v(...);
-  static *** isLoggable(...);
-}
--assumenosideeffects class android.util.Slog {
-  static *** v(...);
-}
--maximumremovedandroidloglevel 2
+
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.DaggerReferenceGlobalRootComponent** { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.DaggerTvGlobalRootComponent** { !synthetic *; }
\ No newline at end of file
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
new file mode 100644
index 0000000..1d008cf
--- /dev/null
+++ b/packages/SystemUI/proguard_common.flags
@@ -0,0 +1,141 @@
+# Preserve line number information for debugging stack traces.
+-keepattributes SourceFile,LineNumberTable
+
+-keep class com.android.systemui.VendorServices
+
+# the `#inject` methods are accessed via reflection to work on ContentProviders
+-keepclassmembers class * extends com.android.systemui.dagger.SysUIComponent { void inject(***); }
+
+# Needed for builds to properly initialize KeyFrames from xml scene
+-keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
+  public <init>();
+}
+
+# Needed to ensure callback field references are kept in their respective
+# owning classes when the downstream callback registrars only store weak refs.
+# TODO(b/264686688): Handle these cases with more targeted annotations.
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  private com.android.keyguard.KeyguardUpdateMonitorCallback *;
+  private com.android.systemui.privacy.PrivacyConfig$Callback *;
+  private com.android.systemui.privacy.PrivacyItemController$Callback *;
+  private com.android.systemui.settings.UserTracker$Callback *;
+  private com.android.systemui.statusbar.phone.StatusBarWindowCallback *;
+  private com.android.systemui.util.service.Observer$Callback *;
+  private com.android.systemui.util.service.ObservableServiceConnection$Callback *;
+}
+# Note that these rules are temporary companions to the above rules, required
+# for cases like Kotlin where fields with anonymous types use the anonymous type
+# rather than the supertype.
+-if class * extends com.android.keyguard.KeyguardUpdateMonitorCallback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+-if class * extends com.android.systemui.privacy.PrivacyConfig$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+-if class * extends com.android.systemui.privacy.PrivacyItemController$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+-if class * extends com.android.systemui.settings.UserTracker$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+-if class * extends com.android.systemui.statusbar.phone.StatusBarWindowCallback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+-if class * extends com.android.systemui.util.service.Observer$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+-if class * extends com.android.systemui.util.service.ObservableServiceConnection$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+  <1> *;
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keep class ** extends androidx.preference.PreferenceFragment
+-keep class com.android.systemui.tuner.*
+
+# The plugins subpackage acts as a shared library that might be referenced in
+# dynamically-loaded plugin APKs.
+-keep class com.android.systemui.plugins.** {
+    *;
+}
+-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
+    *;
+}
+-keep class androidx.core.app.CoreComponentFactory
+
+# Keep the wm shell lib
+-keep class com.android.wm.shell.*
+# Keep the protolog group methods that are called by the generated code
+-keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
+    *;
+}
+
+# Prevent optimization or access modification of any referenced code that may
+# conflict with code in the bootclasspath.
+# TODO(b/222468116): Resolve such collisions in the build system.
+-keepnames class android.**.nano.** { *; }
+-keepnames class com.android.**.nano.** { *; }
+-keepnames class com.android.internal.protolog.** { *; }
+-keepnames class android.hardware.common.** { *; }
+
+# Allows proguard to make private and protected methods and fields public as
+# part of optimization. This lets proguard inline trivial getter/setter methods.
+-allowaccessmodification
+
+# Removes runtime checks added through Kotlin to JVM code genereration to
+# avoid linear growth as more Kotlin code is converted / added to the codebase.
+# These checks are generally applied to Java platform types (values returned
+# from Java code that don't have nullness annotations), but we remove them to
+# avoid code size increases.
+#
+# See also https://kotlinlang.org/docs/reference/java-interop.html
+#
+# TODO(b/199941987): Consider standardizing these rules in a central place as
+# Kotlin gains adoption with other platform targets.
+-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
+    # Remove check for method parameters being null
+    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
+
+    # When a Java platform type is returned and passed to Kotlin NonNull method,
+    # remove the null check
+    static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
+    static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
+
+    # Remove check that final value returned from method is null, if passing
+    # back Java platform type.
+    static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+    static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
+
+    # Null check for accessing a field from a parent class written in Java.
+    static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+    static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
+
+    # Removes code generated from !! operator which converts Nullable type to
+    # NonNull type. These would throw an NPE immediate after on access.
+    static void checkNotNull(java.lang.Object, java.lang.String);
+    static void checkNotNullParameter(java.lang.Object, java.lang.String);
+
+    # Removes lateinit var check being used before being set. Check is applied
+    # on every field access without this.
+    static void throwUninitializedPropertyAccessException(java.lang.String);
+}
+
+
+# Strip verbose logs.
+-assumenosideeffects class android.util.Log {
+  static *** v(...);
+  static *** isLoggable(...);
+}
+-assumenosideeffects class android.util.Slog {
+  static *** v(...);
+}
+-maximumremovedandroidloglevel 2
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 26502f1..bbac7b0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2467,7 +2467,7 @@
     <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
     <string name="controls_favorite_default_title">Controls</string>
     <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
-    <string name="controls_favorite_subtitle">Choose controls to access from Quick Settings</string>
+    <string name="controls_favorite_subtitle">Choose device controls to access quickly</string>
     <!-- Controls management editing screen, user direction for rearranging controls [CHAR LIMIT=NONE] -->
     <string name="controls_favorite_rearrange">Hold &amp; drag to rearrange controls</string>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
new file mode 100644
index 0000000..8f8bff8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.shared.condition
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/** Converts a boolean flow to a [Condition] object which can be used with a [Monitor] */
+@JvmOverloads
+fun Flow<Boolean>.toCondition(scope: CoroutineScope, initialValue: Boolean? = null): Condition {
+    return object : Condition(initialValue, false) {
+        var job: Job? = null
+
+        override fun start() {
+            job = scope.launch { collect { updateCondition(it) } }
+        }
+
+        override fun stop() {
+            job?.cancel()
+            job = null
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 7262a73..8b87e2a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -99,8 +99,10 @@
                 value.initialize(resources, dozeAmount, 0f)
 
                 if (regionSamplingEnabled) {
-                    clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
-                    clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+                    clock?.run {
+                        smallClock.view.addOnLayoutChangeListener(mLayoutChangedListener)
+                        largeClock.view.addOnLayoutChangeListener(mLayoutChangedListener)
+                    }
                 } else {
                     updateColors()
                 }
@@ -175,15 +177,17 @@
     private fun updateColors() {
         val wallpaperManager = WallpaperManager.getInstance(context)
         if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) {
-            if (regionSampler != null) {
-                if (regionSampler?.sampledView == clock?.smallClock?.view) {
-                    smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark
-                    clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
-                    return
-                } else if (regionSampler?.sampledView == clock?.largeClock?.view) {
-                    largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark
-                    clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
-                    return
+            regionSampler?.let { regionSampler ->
+                clock?.let { clock ->
+                    if (regionSampler.sampledView == clock.smallClock.view) {
+                        smallClockIsDark = regionSampler.currentRegionDarkness().isDark
+                        clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+                        return@updateColors
+                    } else if (regionSampler.sampledView == clock.largeClock.view) {
+                        largeClockIsDark = regionSampler.currentRegionDarkness().isDark
+                        clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+                        return@updateColors
+                    }
                 }
             }
         }
@@ -193,8 +197,10 @@
         smallClockIsDark = isLightTheme.data == 0
         largeClockIsDark = isLightTheme.data == 0
 
-        clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
-        clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
+        clock?.run {
+            smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+            largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+        }
     }
 
     private fun updateRegionSampler(sampledRegion: View) {
@@ -240,7 +246,7 @@
     private val configListener =
         object : ConfigurationController.ConfigurationListener {
             override fun onThemeChanged() {
-                clock?.events?.onColorPaletteChanged(resources)
+                clock?.run { events.onColorPaletteChanged(resources) }
                 updateColors()
             }
 
@@ -253,7 +259,10 @@
         object : BatteryStateChangeCallback {
             override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
                 if (isKeyguardVisible && !isCharging && charging) {
-                    clock?.animations?.charge()
+                    clock?.run {
+                        smallClock.animations.charge()
+                        largeClock.animations.charge()
+                    }
                 }
                 isCharging = charging
             }
@@ -262,7 +271,7 @@
     private val localeBroadcastReceiver =
         object : BroadcastReceiver() {
             override fun onReceive(context: Context, intent: Intent) {
-                clock?.events?.onLocaleChanged(Locale.getDefault())
+                clock?.run { events.onLocaleChanged(Locale.getDefault()) }
             }
         }
 
@@ -272,7 +281,10 @@
                 isKeyguardVisible = visible
                 if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
                     if (!isKeyguardVisible) {
-                        clock?.animations?.doze(if (isDozing) 1f else 0f)
+                        clock?.run {
+                            smallClock.animations.doze(if (isDozing) 1f else 0f)
+                            largeClock.animations.doze(if (isDozing) 1f else 0f)
+                        }
                     }
                 }
 
@@ -281,19 +293,19 @@
             }
 
             override fun onTimeFormatChanged(timeFormat: String?) {
-                clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+                clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
             }
 
             override fun onTimeZoneChanged(timeZone: TimeZone) {
-                clock?.events?.onTimeZoneChanged(timeZone)
+                clock?.run { events.onTimeZoneChanged(timeZone) }
             }
 
             override fun onUserSwitchComplete(userId: Int) {
-                clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+                clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
             }
 
             override fun onWeatherDataChanged(data: WeatherData) {
-                clock?.events?.onWeatherDataChanged(data)
+                clock?.run { events.onWeatherDataChanged(data) }
             }
         }
 
@@ -349,34 +361,33 @@
         smallTimeListener = null
         largeTimeListener = null
 
-        clock?.smallClock?.let {
-            smallTimeListener = TimeListener(it, mainExecutor)
-            smallTimeListener?.update(shouldTimeListenerRun)
-        }
-        clock?.largeClock?.let {
-            largeTimeListener = TimeListener(it, mainExecutor)
-            largeTimeListener?.update(shouldTimeListenerRun)
+        clock?.let {
+            smallTimeListener = TimeListener(it.smallClock, mainExecutor).apply {
+                update(shouldTimeListenerRun)
+            }
+            largeTimeListener = TimeListener(it.largeClock, mainExecutor).apply {
+                update(shouldTimeListenerRun)
+            }
         }
     }
 
     private fun updateFontSizes() {
-        clock
-            ?.smallClock
-            ?.events
-            ?.onFontSettingChanged(
+        clock?.run {
+            smallClock.events.onFontSettingChanged(
                 resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
             )
-        clock
-            ?.largeClock
-            ?.events
-            ?.onFontSettingChanged(
+            largeClock.events.onFontSettingChanged(
                 resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
             )
+        }
     }
 
     private fun handleDoze(doze: Float) {
         dozeAmount = doze
-        clock?.animations?.doze(dozeAmount)
+        clock?.run {
+            smallClock.animations.doze(dozeAmount)
+            largeClock.animations.doze(dozeAmount)
+        }
         smallTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
         largeTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5ba0ad6..a6c782d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -26,8 +26,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import kotlin.Unit;
-
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
@@ -38,6 +36,8 @@
 
     private static final long CLOCK_OUT_MILLIS = 150;
     private static final long CLOCK_IN_MILLIS = 200;
+    public static final long CLOCK_IN_START_DELAY_MILLIS = CLOCK_OUT_MILLIS / 2;
+    private static final long STATUS_AREA_START_DELAY_MILLIS = 50;
     private static final long STATUS_AREA_MOVE_MILLIS = 350;
 
     @IntDef({LARGE, SMALL})
@@ -173,7 +173,7 @@
                 msg.setBool1(useLargeClock);
                 msg.setBool2(animate);
                 msg.setBool3(mChildrenAreLaidOut);
-                return Unit.INSTANCE;
+                return kotlin.Unit.INSTANCE;
             }, (msg) -> "updateClockViews"
                     + "; useLargeClock=" + msg.getBool1()
                     + "; animate=" + msg.getBool2()
@@ -235,7 +235,7 @@
         mClockInAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         mClockInAnim.playTogether(ObjectAnimator.ofFloat(in, View.ALPHA, 1f),
                 ObjectAnimator.ofFloat(in, View.TRANSLATION_Y, direction * mClockSwitchYAmount, 0));
-        mClockInAnim.setStartDelay(CLOCK_OUT_MILLIS / 2);
+        mClockInAnim.setStartDelay(CLOCK_IN_START_DELAY_MILLIS);
         mClockInAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
                 mClockInAnim = null;
@@ -247,6 +247,7 @@
 
         mStatusAreaAnim = ObjectAnimator.ofFloat(mStatusArea, View.TRANSLATION_Y,
                 statusAreaYTranslation);
+        mStatusAreaAnim.setStartDelay(useLargeClock ? STATUS_AREA_START_DELAY_MILLIS : 0L);
         mStatusAreaAnim.setDuration(STATUS_AREA_MOVE_MILLIS);
         mStatusAreaAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ad333b7..a34c9fa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -53,11 +53,11 @@
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.PrintWriter;
 import java.util.Locale;
-import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -98,7 +98,7 @@
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
 
     private boolean mOnlyClock = false;
-    private final Executor mUiExecutor;
+    private final DelayableExecutor mUiExecutor;
     private boolean mCanShowDoubleLineClock = true;
     private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
         @Override
@@ -133,7 +133,7 @@
             LockscreenSmartspaceController smartspaceController,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             SecureSettings secureSettings,
-            @Main Executor uiExecutor,
+            @Main DelayableExecutor uiExecutor,
             DumpManager dumpManager,
             ClockEventController clockEventController,
             @KeyguardClockLog LogBuffer logBuffer) {
@@ -344,7 +344,8 @@
         ClockController clock = getClock();
         boolean appeared = mView.switchToClock(clockSize, animate);
         if (clock != null && animate && appeared && clockSize == LARGE) {
-            clock.getAnimations().enter();
+            mUiExecutor.executeDelayed(() -> clock.getLargeClock().getAnimations().enter(),
+                    KeyguardClockSwitch.CLOCK_IN_START_DELAY_MILLIS);
         }
     }
 
@@ -354,7 +355,8 @@
     public void animateFoldToAod(float foldFraction) {
         ClockController clock = getClock();
         if (clock != null) {
-            clock.getAnimations().fold(foldFraction);
+            clock.getSmallClock().getAnimations().fold(foldFraction);
+            clock.getLargeClock().getAnimations().fold(foldFraction);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 0cdef4d..edfcb8d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -349,7 +349,7 @@
 
         ClockController clock = mKeyguardClockSwitchController.getClock();
         boolean customClockAnimation = clock != null
-                && clock.getConfig().getHasCustomPositionUpdatedAnimation();
+                && clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation();
 
         if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
             // Find the clock, so we can exclude it from this transition.
@@ -436,7 +436,8 @@
                     return;
                 }
 
-                clock.getAnimations().onPositionUpdated(from, to, animation.getAnimatedFraction());
+                clock.getLargeClock().getAnimations()
+                        .onPositionUpdated(from, to, animation.getAnimatedFraction());
             });
 
             return anim;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c48aaf4..ea04376 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1771,10 +1771,6 @@
                         MSG_TIMEZONE_UPDATE, intent.getStringExtra(Intent.EXTRA_TIMEZONE));
                 mHandler.sendMessage(msg);
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-                // Clear incompatible charger state when device is unplugged.
-                if (!BatteryStatus.isPluggedIn(intent)) {
-                    mIncompatibleCharger = false;
-                }
                 final Message msg = mHandler.obtainMessage(
                         MSG_BATTERY_UPDATE, new BatteryStatus(intent, mIncompatibleCharger));
                 mHandler.sendMessage(msg);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 7661b8d..281067d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -23,8 +23,8 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -178,7 +178,7 @@
      * Registers the CentralSurfaces to which this Keyguard View is mounted.
      */
     void registerCentralSurfaces(CentralSurfaces centralSurfaces,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             @Nullable ShadeExpansionStateManager shadeExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer,
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index cde8ff7..6740375 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -27,7 +27,9 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.log.dagger.KeyguardClockLog;
 import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.log.LogBuffer;
 import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.shared.clocks.DefaultClockProvider;
 
@@ -51,7 +53,8 @@
             @Background CoroutineDispatcher bgDispatcher,
             FeatureFlags featureFlags,
             @Main Resources resources,
-            LayoutInflater layoutInflater) {
+            LayoutInflater layoutInflater,
+            @KeyguardClockLog LogBuffer logBuffer) {
         ClockRegistry registry = new ClockRegistry(
                 context,
                 pluginManager,
@@ -62,6 +65,7 @@
                 /* handleAllUsers= */ true,
                 new DefaultClockProvider(context, layoutInflater, resources),
                 context.getString(R.string.lockscreen_clock_id_fallback),
+                logBuffer,
                 /* keepAllLoaded = */ false,
                 /* subTag = */ "System");
         registry.registerListeners();
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
index d01c98a..91dd1d6 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
@@ -17,7 +17,7 @@
 package com.android.keyguard.dagger;
 
 import com.android.keyguard.KeyguardStatusViewController;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
 
@@ -37,8 +37,8 @@
     interface Factory {
         KeyguardStatusBarViewComponent build(
                 @BindsInstance KeyguardStatusBarView view,
-                @BindsInstance NotificationPanelViewController.NotificationPanelViewStateProvider
-                        notificationPanelViewStateProvider);
+                @BindsInstance ShadeViewStateProvider
+                        shadeViewStateProvider);
     }
 
     /** Builds a {@link KeyguardStatusViewController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
new file mode 100644
index 0000000..ae9f57f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.accessibility.data.repository
+
+import android.view.accessibility.AccessibilityManager
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+
+/** Exposes accessibility-related state. */
+interface AccessibilityRepository {
+    /** @see [AccessibilityManager.isTouchExplorationEnabled] */
+    val isTouchExplorationEnabled: Flow<Boolean>
+
+    companion object {
+        operator fun invoke(a11yManager: AccessibilityManager): AccessibilityRepository =
+            AccessibilityRepositoryImpl(a11yManager)
+    }
+}
+
+private class AccessibilityRepositoryImpl(
+    manager: AccessibilityManager,
+) : AccessibilityRepository {
+    override val isTouchExplorationEnabled: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val listener = TouchExplorationStateChangeListener(::trySend)
+                manager.addTouchExplorationStateChangeListener(listener)
+                trySend(manager.isTouchExplorationEnabled)
+                awaitClose { manager.removeTouchExplorationStateChangeListener(listener) }
+            }
+            .distinctUntilChanged()
+}
+
+@Module
+object AccessibilityRepositoryModule {
+    @Provides fun provideRepo(manager: AccessibilityManager) = AccessibilityRepository(manager)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
new file mode 100644
index 0000000..968ce0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.accessibility.domain.interactor
+
+import com.android.systemui.accessibility.data.repository.AccessibilityRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class AccessibilityInteractor
+@Inject
+constructor(
+    private val a11yRepo: AccessibilityRepository,
+) {
+    /** @see [android.view.accessibility.AccessibilityManager.isTouchExplorationEnabled] */
+    val isTouchExplorationEnabled: Flow<Boolean>
+        get() = a11yRepo.isTouchExplorationEnabled
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
index 7f5a67f..57ffd24 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
@@ -19,6 +19,8 @@
 import android.content.Context
 import android.hardware.biometrics.BiometricAuthenticator.Modality
 import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
+import android.hardware.biometrics.BiometricConstants
+import android.hardware.face.FaceManager
 import android.util.AttributeSet
 import com.android.systemui.R
 
@@ -27,6 +29,7 @@
     context: Context,
     attrs: AttributeSet?
 ) : AuthBiometricFingerprintView(context, attrs) {
+    var isFaceClass3 = false
 
     constructor (context: Context) : this(context, null)
 
@@ -34,10 +37,22 @@
 
     override fun forceRequireConfirmation(@Modality modality: Int) = modality == TYPE_FACE
 
-    override fun ignoreUnsuccessfulEventsFrom(@Modality modality: Int) = modality == TYPE_FACE
+    override fun ignoreUnsuccessfulEventsFrom(@Modality modality: Int, unsuccessfulReason: String) =
+        modality == TYPE_FACE && !(isFaceClass3 && isLockoutErrorString(unsuccessfulReason))
 
     override fun onPointerDown(failedModalities: Set<Int>) = failedModalities.contains(TYPE_FACE)
 
     override fun createIconController(): AuthIconController =
         AuthBiometricFingerprintAndFaceIconController(mContext, mIconView, mIconViewOverlay)
+
+    private fun isLockoutErrorString(unsuccessfulReason: String) =
+        unsuccessfulReason == FaceManager.getErrorString(
+            mContext,
+            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+            0 /*vendorCode */
+        ) || unsuccessfulReason == FaceManager.getErrorString(
+            mContext,
+            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+            0 /*vendorCode */
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 13bb6d3..e04dd06 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -258,7 +258,8 @@
     }
 
     /** Ignore all events from this (secondary) modality except successful authentication. */
-    protected boolean ignoreUnsuccessfulEventsFrom(@Modality int modality) {
+    protected boolean ignoreUnsuccessfulEventsFrom(@Modality int modality,
+            String unsuccessfulReason) {
         return false;
     }
 
@@ -541,7 +542,7 @@
      */
     public void onAuthenticationFailed(
             @Modality int modality, @Nullable String failureReason) {
-        if (ignoreUnsuccessfulEventsFrom(modality)) {
+        if (ignoreUnsuccessfulEventsFrom(modality, failureReason)) {
             return;
         }
 
@@ -556,7 +557,7 @@
      * @param error message
      */
     public void onError(@Modality int modality, String error) {
-        if (ignoreUnsuccessfulEventsFrom(modality)) {
+        if (ignoreUnsuccessfulEventsFrom(modality, error)) {
             return;
         }
 
@@ -586,7 +587,7 @@
      * @param help message
      */
     public void onHelp(@Modality int modality, String help) {
-        if (ignoreUnsuccessfulEventsFrom(modality)) {
+        if (ignoreUnsuccessfulEventsFrom(modality, help)) {
             return;
         }
         if (mSize != AuthDialog.SIZE_MEDIUM) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 517f94f..aeebb01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
 import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
 
@@ -383,6 +384,8 @@
                 fingerprintAndFaceView.setSensorProperties(fpProperties);
                 fingerprintAndFaceView.setScaleFactorProvider(config.mScaleProvider);
                 fingerprintAndFaceView.updateOverrideIconLayoutParamsSize();
+                fingerprintAndFaceView.setFaceClass3(
+                        faceProperties.sensorStrength == STRENGTH_STRONG);
                 mBiometricView = fingerprintAndFaceView;
             } else if (fpProperties != null) {
                 final AuthBiometricFingerprintView fpView =
@@ -877,7 +880,7 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
                 windowFlags,
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0999229..6eb3c70 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -1349,7 +1349,7 @@
         default void onEnrollmentsChanged(@Modality int modality) {}
 
         /**
-         * Called when UDFPS enrollments have changed. This is called after boot and on changes to
+         * Called when enrollments have changed. This is called after boot and on changes to
          * enrollment.
          */
         default void onEnrollmentsChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index 6670108..08e1e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -26,9 +26,6 @@
     void onSuccessfulUnlock();
 
     /** */
-    void onNotificationActive();
-
-    /** */
     void setShowingAod(boolean showingAod);
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index cc25368..f335d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -25,10 +25,6 @@
     }
 
     @Override
-    public void onNotificationActive() {
-    }
-
-    @Override
     public void setShowingAod(boolean showingAod) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 8bdef13..6a021f6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -174,10 +174,6 @@
     }
 
     @Override
-    public void onNotificationActive() {
-    }
-
-    @Override
     public void setShowingAod(boolean showingAod) {
         mShowingAod = showingAod;
         updateSessionActive();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 5230159..0aeab10 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -32,7 +32,6 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
-import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -277,7 +276,7 @@
         } else if (!mIsMinimized) {
             setExpandedView();
         }
-        if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && mClipboardModel.isRemote()) {
+        if (mClipboardModel.isRemote()) {
             mTimeoutHandler.cancelTimeout();
             mOnUiUpdate = null;
         } else {
@@ -291,8 +290,7 @@
         mView.setMinimized(false);
         switch (model.getType()) {
             case TEXT:
-                if ((mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && model.isRemote())
-                        || DeviceConfig.getBoolean(
+                if (model.isRemote() || DeviceConfig.getBoolean(
                         DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
                     if (model.getTextLinks() != null) {
                         classifyText(model);
@@ -326,11 +324,7 @@
                 mView.showDefaultTextPreview();
                 break;
         }
-        if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR)) {
-            if (!model.isRemote()) {
-                maybeShowRemoteCopy(model.getClipData());
-            }
-        } else {
+        if (!model.isRemote()) {
             maybeShowRemoteCopy(model.getClipData());
         }
         if (model.getType() != ClipboardModel.Type.OTHER) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index ac91665..75f70ae 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.accessibility.AccessibilityModule;
+import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
 import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
@@ -47,7 +48,6 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.FlagsModule;
-import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyboard.KeyboardModule;
 import com.android.systemui.keyguard.data.BouncerViewModule;
 import com.android.systemui.log.dagger.LogModule;
@@ -63,6 +63,7 @@
 import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
 import com.android.systemui.qs.FgsManagerController;
 import com.android.systemui.qs.FgsManagerControllerImpl;
+import com.android.systemui.qs.QSFragmentStartableModule;
 import com.android.systemui.qs.footer.dagger.FooterActionsModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenrecord.ScreenRecordModule;
@@ -140,6 +141,7 @@
  */
 @Module(includes = {
             AccessibilityModule.class,
+            AccessibilityRepositoryModule.class,
             AppOpsModule.class,
             AssistModule.class,
             BiometricsModule.class,
@@ -167,6 +169,7 @@
             PolicyModule.class,
             PrivacyModule.class,
             QRCodeScannerModule.class,
+            QSFragmentStartableModule.class,
             ScreenshotModule.class,
             SensorModule.class,
             SecurityRepositoryModule.class,
@@ -196,8 +199,7 @@
             DozeComponent.class,
             ExpandableNotificationRowComponent.class,
             KeyguardBouncerComponent.class,
-            NotificationShelfComponent.class,
-            FragmentService.FragmentCreator.class
+            NotificationShelfComponent.class
         })
 public abstract class SystemUIModule {
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
index 58b70b0..99451f2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
@@ -23,7 +23,7 @@
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
 import java.util.Optional;
@@ -54,8 +54,8 @@
         }
 
         session.registerInputListener(ev -> {
-            final NotificationPanelViewController viewController =
-                    mSurfaces.map(CentralSurfaces::getNotificationPanelViewController).orElse(null);
+            final ShadeViewController viewController =
+                    mSurfaces.map(CentralSurfaces::getShadeViewController).orElse(null);
 
             if (viewController != null) {
                 viewController.handleExternalTouch((MotionEvent) ev);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
index 081bab0..5f03743 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java
@@ -25,16 +25,16 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.touch.BouncerSwipeTouchHandler;
 import com.android.systemui.dreams.touch.DreamTouchHandler;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
-import javax.inject.Named;
-import javax.inject.Provider;
-
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoSet;
 
+import javax.inject.Named;
+import javax.inject.Provider;
+
 /**
  * This module captures the components associated with {@link BouncerSwipeTouchHandler}.
  */
@@ -78,8 +78,8 @@
         return flingAnimationUtilsBuilderProvider.get()
                 .reset()
                 .setMaxLengthSeconds(
-                        NotificationPanelViewController.FLING_CLOSING_MAX_LENGTH_SECONDS)
-                .setSpeedUpFactor(NotificationPanelViewController.FLING_SPEED_UP_FACTOR)
+                        ShadeViewController.FLING_CLOSING_MAX_LENGTH_SECONDS)
+                .setSpeedUpFactor(ShadeViewController.FLING_SPEED_UP_FACTOR)
                 .build();
     }
 
@@ -92,8 +92,8 @@
             Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) {
         return flingAnimationUtilsBuilderProvider.get()
                 .reset()
-                .setMaxLengthSeconds(NotificationPanelViewController.FLING_MAX_LENGTH_SECONDS)
-                .setSpeedUpFactor(NotificationPanelViewController.FLING_SPEED_UP_FACTOR)
+                .setMaxLengthSeconds(ShadeViewController.FLING_MAX_LENGTH_SECONDS)
+                .setSpeedUpFactor(ShadeViewController.FLING_SPEED_UP_FACTOR)
                 .build();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index bd982c6..29c63bb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -58,12 +58,6 @@
             "notification_drag_to_contents"
         )
 
-    // TODO(b/254512517): Tracking Bug
-    val FSI_REQUIRES_KEYGUARD = releasedFlag(110, "fsi_requires_keyguard")
-
-    // TODO(b/259130119): Tracking Bug
-    val FSI_ON_DND_UPDATE = releasedFlag(259130119, "fsi_on_dnd_update")
-
     // TODO(b/254512538): Tracking Bug
     val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply")
 
@@ -74,31 +68,15 @@
     val NOTIFICATION_MEMORY_LOGGING_ENABLED =
         unreleasedFlag(119, "notification_memory_logging_enabled")
 
-    // TODO(b/254512731): Tracking Bug
-    @JvmField val NOTIFICATION_DISMISSAL_FADE = releasedFlag(113, "notification_dismissal_fade")
-
     @JvmField val USE_ROUNDNESS_SOURCETYPES = releasedFlag(116, "use_roundness_sourcetype")
 
-    // TODO(b/259217907)
-    @JvmField
-    val NOTIFICATION_GROUP_DISMISSAL_ANIMATION =
-        releasedFlag(259217907, "notification_group_dismissal_animation")
-
     @JvmField
     val SIMPLIFIED_APPEAR_FRACTION =
-        unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true)
+        releasedFlag(259395680, "simplified_appear_fraction")
 
     // TODO(b/257315550): Tracking Bug
     val NO_HUN_FOR_OLD_WHEN = releasedFlag(118, "no_hun_for_old_when")
 
-    // TODO(b/260335638): Tracking Bug
-    @JvmField
-    val NOTIFICATION_INLINE_REPLY_ANIMATION =
-        releasedFlag(174148361, "notification_inline_reply_animation")
-
-    val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
-        releasedFlag(254647461, "filter_unseen_notifs_on_keyguard")
-
     // TODO(b/277338665): Tracking Bug
     @JvmField
     val NOTIFICATION_SHELF_REFACTOR =
@@ -329,8 +307,7 @@
         unreleasedFlag(611, "new_status_bar_icons_debug_coloring")
 
     // TODO(b/265892345): Tracking Bug
-    val PLUG_IN_STATUS_BAR_CHIP =
-            unreleasedFlag(265892345, "plug_in_status_bar_chip", teamfood = true)
+    val PLUG_IN_STATUS_BAR_CHIP = releasedFlag(265892345, "plug_in_status_bar_chip")
 
     // 700 - dialer/calls
     // TODO(b/254512734): Tracking Bug
@@ -406,7 +383,7 @@
     val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations")
 
     // TODO(b/270437894): Tracking Bug
-    val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume", teamfood = true)
+    val MEDIA_REMOTE_RESUME = releasedFlag(917, "media_remote_resume")
 
     // 1000 - dock
     val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
@@ -614,8 +591,6 @@
     @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
 
     // 1800 - shade container
-    @JvmField
-    val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport")
     // TODO(b/265944639): Tracking Bug
     @JvmField val DUAL_SHADE = unreleasedFlag(1801, "dual_shade")
 
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 6a27ee7..81a5206 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -39,16 +39,17 @@
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.util.leak.LeakDetector;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.HashMap;
-
 import dagger.assisted.Assisted;
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.inject.Provider;
+
 public class FragmentHostManager {
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -322,25 +323,17 @@
             return instantiateWithInjections(context, className, arguments);
         }
 
-        private Fragment instantiateWithInjections(
-                Context context, String className, Bundle args) {
-            FragmentService.FragmentInstantiationInfo fragmentInstantiationInfo =
+        private Fragment instantiateWithInjections(Context context, String className, Bundle args) {
+            Provider<? extends Fragment> fragmentProvider =
                     mManager.getInjectionMap().get(className);
-            if (fragmentInstantiationInfo != null) {
-                try {
-                    Fragment f = (Fragment) fragmentInstantiationInfo
-                            .mMethod
-                            .invoke(fragmentInstantiationInfo.mDaggerComponent);
-                    // Setup the args, taken from Fragment#instantiate.
-                    if (args != null) {
-                        args.setClassLoader(f.getClass().getClassLoader());
-                        f.setArguments(args);
-                    }
-                    return f;
-                } catch (IllegalAccessException | InvocationTargetException e) {
-                    throw new Fragment.InstantiationException("Unable to instantiate " + className,
-                            e);
+            if (fragmentProvider != null) {
+                Fragment f = fragmentProvider.get();
+                // Setup the args, taken from Fragment#instantiate.
+                if (args != null) {
+                    args.setClassLoader(f.getClass().getClassLoader());
+                    f.setArguments(args);
                 }
+                return f;
             }
             return Fragment.instantiate(context, className, args);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index d302b13a..a75c056 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -24,16 +24,12 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.qs.QSFragment;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.PrintWriter;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 
 import javax.inject.Inject;
-
-import dagger.Subcomponent;
+import javax.inject.Provider;
 
 /**
  * Holds a map of root views to FragmentHostStates and generates them as needed.
@@ -49,9 +45,9 @@
      * A map with the means to create fragments via Dagger injection.
      *
      * key: the fragment class name.
-     * value: see {@link FragmentInstantiationInfo}.
+     * value: A {@link Provider} for the Fragment
      */
-    private final ArrayMap<String, FragmentInstantiationInfo> mInjectionMap = new ArrayMap<>();
+    private final ArrayMap<String, Provider<? extends Fragment>> mInjectionMap = new ArrayMap<>();
     private final Handler mHandler = new Handler();
     private final FragmentHostManager.Factory mFragmentHostManagerFactory;
 
@@ -67,38 +63,31 @@
 
     @Inject
     public FragmentService(
-            FragmentCreator.Factory fragmentCreatorFactory,
             FragmentHostManager.Factory fragmentHostManagerFactory,
             ConfigurationController configurationController,
             DumpManager dumpManager) {
         mFragmentHostManagerFactory = fragmentHostManagerFactory;
-        addFragmentInstantiationProvider(fragmentCreatorFactory.build());
         configurationController.addCallback(mConfigurationListener);
 
-        dumpManager.registerDumpable(getClass().getSimpleName(), this);
+        dumpManager.registerNormalDumpable(this);
     }
 
-    ArrayMap<String, FragmentInstantiationInfo> getInjectionMap() {
+    ArrayMap<String, Provider<? extends Fragment>> getInjectionMap() {
         return mInjectionMap;
     }
 
     /**
      * Adds a new Dagger component object that provides method(s) to create fragments via injection.
      */
-    public void addFragmentInstantiationProvider(Object daggerComponent) {
-        for (Method method : daggerComponent.getClass().getDeclaredMethods()) {
-            if (Fragment.class.isAssignableFrom(method.getReturnType())
-                    && (method.getModifiers() & Modifier.PUBLIC) != 0) {
-                String fragmentName = method.getReturnType().getName();
-                if (mInjectionMap.containsKey(fragmentName)) {
-                    Log.w(TAG, "Fragment " + fragmentName + " is already provided by different"
-                            + " Dagger component; Not adding method");
-                    continue;
-                }
-                mInjectionMap.put(
-                        fragmentName, new FragmentInstantiationInfo(method, daggerComponent));
-            }
+    public void addFragmentInstantiationProvider(
+            Class<?> fragmentCls, Provider<? extends Fragment> provider) {
+        String fragmentName = fragmentCls.getName();
+        if (mInjectionMap.containsKey(fragmentName)) {
+            Log.w(TAG, "Fragment " + fragmentName + " is already provided by different"
+                    + " Dagger component; Not adding method");
+            return;
         }
+        mInjectionMap.put(fragmentName, provider);
     }
 
     public FragmentHostManager getFragmentHostManager(View view) {
@@ -132,22 +121,6 @@
         }
     }
 
-    /**
-     * The subcomponent of dagger that holds all fragments that need injection.
-     */
-    @Subcomponent
-    public interface FragmentCreator {
-        /** Factory for creating a FragmentCreator. */
-        @Subcomponent.Factory
-        interface Factory {
-            FragmentCreator build();
-        }
-        /**
-         * Inject a QSFragment.
-         */
-        QSFragment createQSFragment();
-    }
-
     private class FragmentHostState {
         private final View mView;
 
@@ -170,16 +143,4 @@
             mFragmentHostManager.onConfigurationChanged(newConfig);
         }
     }
-
-    /** An object containing the information needed to instantiate a fragment. */
-    static class FragmentInstantiationInfo {
-        /** The method that returns a newly-created fragment of the given class. */
-        final Method mMethod;
-        /** The Dagger component that the method should be invoked on. */
-        final Object mDaggerComponent;
-        FragmentInstantiationInfo(Method method, Object daggerComponent) {
-            this.mMethod = method;
-            this.mDaggerComponent = daggerComponent;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 4be47ec..d3b6fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -1043,9 +1043,6 @@
                             Log.w(TAG, "Bugreport handler could not be launched");
                             mIActivityManager.requestInteractiveBugReport();
                         }
-                        // Maybe close shade (depends on a flag) so user sees the activity
-                        mCentralSurfacesOptional.ifPresent(
-                                CentralSurfaces::collapseShadeForBugreport);
                     } catch (RemoteException e) {
                     }
                 }
@@ -1064,8 +1061,6 @@
                 mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
                 mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
                 mIActivityManager.requestFullBugReport();
-                // Maybe close shade (depends on a flag) so user sees the activity
-                mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShadeForBugreport);
             } catch (RemoteException e) {
             }
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0fd479a..e0af5ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -134,9 +134,9 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -3098,7 +3098,7 @@
      * @return the View Controller for the Keyguard View this class is mediating.
      */
     public KeyguardViewController registerCentralSurfaces(CentralSurfaces centralSurfaces,
-            NotificationPanelViewController panelView,
+            ShadeViewController panelView,
             @Nullable ShadeExpansionStateManager shadeExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer, KeyguardBypassController bypassController) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 09002fd..0055f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -212,14 +212,9 @@
                             userId: Int,
                             hasEnrollments: Boolean
                         ) {
-                            // TODO(b/242022358), use authController.isFaceAuthEnrolled after
-                            //  ag/20176811 is available.
-                            if (
-                                sensorBiometricType == BiometricType.FACE &&
-                                    userId == selectedUserId
-                            ) {
+                            if (sensorBiometricType == BiometricType.FACE) {
                                 trySendWithFailureLogging(
-                                    hasEnrollments,
+                                    authController.isFaceAuthEnrolled(selectedUserId),
                                     TAG,
                                     "Face enrollment changed"
                                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 5f6098b..05ab01b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -140,8 +140,10 @@
     private var authCancellationSignal: CancellationSignal? = null
     private var detectCancellationSignal: CancellationSignal? = null
     private var faceAcquiredInfoIgnoreList: Set<Int>
+    private var retryCount = 0
 
     private var cancelNotReceivedHandlerJob: Job? = null
+    private var halErrorRetryJob: Job? = null
 
     private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
         MutableStateFlow(null)
@@ -228,6 +230,8 @@
             .onEach { goingAwayOrUserSwitchingInProgress ->
                 if (goingAwayOrUserSwitchingInProgress) {
                     _isAuthenticated.value = false
+                    retryCount = 0
+                    halErrorRetryJob?.cancel()
                 }
             }
             .launchIn(applicationScope)
@@ -385,14 +389,11 @@
                 _authenticationStatus.value = errorStatus
                 _isAuthenticated.value = false
                 if (errorStatus.isCancellationError()) {
-                    cancelNotReceivedHandlerJob?.cancel()
-                    applicationScope.launch {
-                        faceAuthLogger.launchingQueuedFaceAuthRequest(
-                            faceAuthRequestedWhileCancellation
-                        )
-                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
-                        faceAuthRequestedWhileCancellation = null
-                    }
+                    handleFaceCancellationError()
+                }
+                if (errorStatus.isHardwareError()) {
+                    faceAuthLogger.hardwareError(errorStatus)
+                    handleFaceHardwareError()
                 }
                 faceAuthLogger.authenticationError(
                     errorCode,
@@ -418,6 +419,35 @@
             }
         }
 
+    private fun handleFaceCancellationError() {
+        cancelNotReceivedHandlerJob?.cancel()
+        applicationScope.launch {
+            faceAuthRequestedWhileCancellation?.let {
+                faceAuthLogger.launchingQueuedFaceAuthRequest(it)
+                authenticate(it)
+            }
+            faceAuthRequestedWhileCancellation = null
+        }
+    }
+
+    private fun handleFaceHardwareError() {
+        if (retryCount < HAL_ERROR_RETRY_MAX) {
+            retryCount++
+            halErrorRetryJob?.cancel()
+            halErrorRetryJob =
+                applicationScope.launch {
+                    delay(HAL_ERROR_RETRY_TIMEOUT)
+                    if (retryCount < HAL_ERROR_RETRY_MAX) {
+                        faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
+                        authenticate(
+                            FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
+                            fallbackToDetection = false
+                        )
+                    }
+                }
+        }
+    }
+
     private fun onFaceAuthRequestCompleted() {
         cancellationInProgress = false
         _isAuthRunning.value = false
@@ -558,6 +588,12 @@
          * cancelled.
          */
         const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
+
+        /** Number of allowed retries whenever there is a face hardware error */
+        const val HAL_ERROR_RETRY_MAX = 20
+
+        /** Timeout before retries whenever there is a HAL error. */
+        const val HAL_ERROR_RETRY_TIMEOUT = 500L // ms
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index eded9c1..c8bd958 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -50,6 +50,11 @@
      * was cancelled before it completed.
      */
     fun isCancellationError() = msgId == FaceManager.FACE_ERROR_CANCELED
+
+    /** Method that checks if [msgId] is a hardware error. */
+    fun isHardwareError() =
+        msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE ||
+            msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS
 }
 
 /** Face detection success message. */
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 7f6e4a9..efd3ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,6 +4,7 @@
 import android.hardware.face.FaceSensorPropertiesInternal
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.log.dagger.FaceAuthLog
 import com.android.systemui.plugins.log.LogBuffer
@@ -239,4 +240,25 @@
             { "Requesting face auth for trigger: $str1" }
         )
     }
+
+    fun hardwareError(errorStatus: ErrorAuthenticationStatus) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = "${errorStatus.msg}"
+                int1 = errorStatus.msgId
+            },
+            { "Received face hardware error: $str1 , code: $int1" }
+        )
+    }
+
+    fun attemptingRetryAfterHardwareError(retryCount: Int) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = retryCount },
+            { "Attempting face auth again because of HW error: retry attempt $int1" }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index 1c8bfd1..8b74263 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -147,6 +147,7 @@
 
         val expandedBottomActionIds =
             setOf(
+                R.id.media_progress_bar,
                 R.id.actionPrev,
                 R.id.actionNext,
                 R.id.action0,
@@ -155,7 +156,22 @@
                 R.id.action3,
                 R.id.action4,
                 R.id.media_scrubbing_elapsed_time,
-                R.id.media_scrubbing_total_time
+                R.id.media_scrubbing_total_time,
+            )
+
+        val detailIds =
+            setOf(
+                R.id.header_title,
+                R.id.header_artist,
+                R.id.media_explicit_indicator,
+                R.id.actionPlayPause,
+            )
+
+        val backgroundIds =
+            setOf(
+                R.id.album_art,
+                R.id.turbulence_noise_view,
+                R.id.touch_ripple_view,
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
index 2509f21..35f5a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
@@ -20,12 +20,14 @@
 import android.media.session.MediaController
 import android.media.session.PlaybackState
 import android.os.SystemClock
+import android.os.Trace
 import android.view.GestureDetector
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewConfiguration
 import android.widget.SeekBar
 import androidx.annotation.AnyThread
+import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
 import androidx.core.view.GestureDetectorCompat
 import androidx.lifecycle.LiveData
@@ -36,10 +38,13 @@
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.util.concurrency.RepeatableExecutor
 import javax.inject.Inject
+import kotlin.math.abs
 
 private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
 private const val MIN_FLING_VELOCITY_SCALE_FACTOR = 10
 
+private const val TRACE_POSITION_NAME = "SeekBarPollingPosition"
+
 private fun PlaybackState.isInMotion(): Boolean {
     return this.state == PlaybackState.STATE_PLAYING ||
         this.state == PlaybackState.STATE_FAST_FORWARDING ||
@@ -295,14 +300,20 @@
     @WorkerThread
     private fun checkIfPollingNeeded() {
         val needed = listening && !scrubbing && playbackState?.isInMotion() ?: false
+        val traceCookie = controller?.sessionToken.hashCode()
         if (needed) {
             if (cancel == null) {
-                cancel =
+                Trace.beginAsyncSection(TRACE_POSITION_NAME, traceCookie)
+                val cancelPolling =
                     bgExecutor.executeRepeatedly(
                         this::checkPlaybackPosition,
                         0L,
                         POSITION_UPDATE_INTERVAL_MILLIS
                     )
+                cancel = Runnable {
+                    cancelPolling.run()
+                    Trace.endAsyncSection(TRACE_POSITION_NAME, traceCookie)
+                }
             }
         } else {
             cancel?.run()
@@ -316,6 +327,10 @@
             return SeekBarChangeListener(this, falsingManager)
         }
 
+    /** first and last motion events of seekbar grab. */
+    @VisibleForTesting var firstMotionEvent: MotionEvent? = null
+    @VisibleForTesting var lastMotionEvent: MotionEvent? = null
+
     /** Attach touch handlers to the seek bar view. */
     fun attachTouchHandlers(bar: SeekBar) {
         bar.setOnSeekBarChangeListener(seekBarListener)
@@ -342,6 +357,23 @@
         }
     }
 
+    /**
+     * This method specifies if user made a bad seekbar grab or not. If the vertical distance from
+     * first touch on seekbar is more than the horizontal distance, this means that the seekbar grab
+     * is more vertical and should be rejected. Seekbar accepts horizontal grabs only.
+     *
+     * Single tap has the same first and last motion event, it is counted as a valid grab.
+     *
+     * @return whether the touch on seekbar is valid.
+     */
+    private fun isValidSeekbarGrab(): Boolean {
+        if (firstMotionEvent == null || lastMotionEvent == null) {
+            return true
+        }
+        return abs(firstMotionEvent!!.x - lastMotionEvent!!.x) >=
+            abs(firstMotionEvent!!.y - lastMotionEvent!!.y)
+    }
+
     /** Listener interface to be notified when the user starts or stops scrubbing. */
     interface ScrubbingChangeListener {
         fun onScrubbingChanged(scrubbing: Boolean)
@@ -367,7 +399,7 @@
         }
 
         override fun onStopTrackingTouch(bar: SeekBar) {
-            if (falsingManager.isFalseTouch(MEDIA_SEEKBAR)) {
+            if (!viewModel.isValidSeekbarGrab() || falsingManager.isFalseTouch(MEDIA_SEEKBAR)) {
                 viewModel.onSeekFalse()
             }
             viewModel.onSeek(bar.progress.toLong())
@@ -415,6 +447,8 @@
                 return false
             }
             detector.onTouchEvent(event)
+            // Store the last motion event done on seekbar.
+            viewModel.lastMotionEvent = event.copy()
             return !shouldGoToSeekBar
         }
 
@@ -459,6 +493,8 @@
             if (shouldGoToSeekBar) {
                 bar.parent?.requestDisallowInterceptTouchEvent(true)
             }
+            // Store the first motion event done on seekbar.
+            viewModel.firstMotionEvent = event.copy()
             return shouldGoToSeekBar
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
index 70f2dee..0b33904 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
@@ -180,5 +180,7 @@
                 R.id.media_cover2_container,
                 R.id.media_cover3_container
             )
+
+        val backgroundId = R.id.sizing_view
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index cd51d92..4bca778 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -61,37 +61,6 @@
 
     companion object {
         @JvmField val GUTS_ANIMATION_DURATION = 500L
-        val controlIds =
-            setOf(
-                R.id.media_progress_bar,
-                R.id.actionNext,
-                R.id.actionPrev,
-                R.id.action0,
-                R.id.action1,
-                R.id.action2,
-                R.id.action3,
-                R.id.action4,
-                R.id.media_scrubbing_elapsed_time,
-                R.id.media_scrubbing_total_time
-            )
-
-        val detailIds =
-            setOf(
-                R.id.header_title,
-                R.id.header_artist,
-                R.id.media_explicit_indicator,
-                R.id.actionPlayPause,
-            )
-
-        val backgroundIds =
-            setOf(
-                R.id.album_art,
-                R.id.turbulence_noise_view,
-                R.id.touch_ripple_view,
-            )
-
-        // Sizing view id for recommendation card view.
-        val recSizingViewId = R.id.sizing_view
     }
 
     /** A listener when the current dimensions of the player change */
@@ -182,15 +151,14 @@
                         lastOrientation = newOrientation
                         // Update the height of media controls for the expanded layout. it is needed
                         // for large screen devices.
-                        if (type == TYPE.PLAYER) {
-                            backgroundIds.forEach { id ->
-                                expandedLayout.getConstraint(id).layout.mHeight =
-                                    context.resources.getDimensionPixelSize(
-                                        R.dimen.qs_media_session_height_expanded
-                                    )
+                        val backgroundIds =
+                            if (type == TYPE.PLAYER) {
+                                MediaViewHolder.backgroundIds
+                            } else {
+                                setOf(RecommendationViewHolder.backgroundId)
                             }
-                        } else {
-                            expandedLayout.getConstraint(recSizingViewId).layout.mHeight =
+                        backgroundIds.forEach { id ->
+                            expandedLayout.getConstraint(id).layout.mHeight =
                                 context.resources.getDimensionPixelSize(
                                     R.dimen.qs_media_session_height_expanded
                                 )
@@ -338,19 +306,19 @@
         squishedViewState.height = squishedHeight
         // We are not overriding the squishedViewStates height but only the children to avoid
         // them remeasuring the whole view. Instead it just remains as the original size
-        backgroundIds.forEach { id ->
+        MediaViewHolder.backgroundIds.forEach { id ->
             squishedViewState.widgetStates.get(id)?.let { state -> state.height = squishedHeight }
         }
 
         // media player
         calculateWidgetGroupAlphaForSquishiness(
-            controlIds,
+            MediaViewHolder.expandedBottomActionIds,
             squishedViewState.measureHeight.toFloat(),
             squishedViewState,
             squishFraction
         )
         calculateWidgetGroupAlphaForSquishiness(
-            detailIds,
+            MediaViewHolder.detailIds,
             squishedViewState.measureHeight.toFloat(),
             squishedViewState,
             squishFraction
@@ -660,7 +628,7 @@
                 result.height = result.measureHeight
                 result.width = result.measureWidth
                 // Make sure all background views are also resized such that their size is correct
-                backgroundIds.forEach { id ->
+                MediaViewHolder.backgroundIds.forEach { id ->
                     result.widgetStates.get(id)?.let { state ->
                         state.height = result.height
                         state.width = result.width
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index e8ef612..b0fb349 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -25,6 +25,7 @@
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -740,7 +741,7 @@
         mView.setComponents(mRecentsOptional);
         if (mCentralSurfacesOptionalLazy.get().isPresent()) {
             mView.setComponents(
-                    mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController());
+                    mCentralSurfacesOptionalLazy.get().get().getShadeViewController());
         }
         mView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer);
         mView.setOnVerticalChangedListener(this::onVerticalChanged);
@@ -1342,8 +1343,8 @@
 
     private void onVerticalChanged(boolean isVertical) {
         Optional<CentralSurfaces> cs = mCentralSurfacesOptionalLazy.get();
-        if (cs.isPresent() && cs.get().getNotificationPanelViewController() != null) {
-            cs.get().getNotificationPanelViewController().setQsScrimEnabled(!isVertical);
+        if (cs.isPresent() && cs.get().getShadeViewController() != null) {
+            cs.get().getShadeViewController().setQsScrimEnabled(!isVertical);
         }
     }
 
@@ -1717,12 +1718,15 @@
         if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
             navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight));
         }
+        final boolean needsScrim = userContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_navBarNeedsScrim);
+        navBarProvider.setFlags(needsScrim ? 0 : FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM);
 
         final InsetsFrameProvider tappableElementProvider = new InsetsFrameProvider(
                 mInsetsSourceOwner, 0, WindowInsets.Type.tappableElement());
-        final boolean navBarTapThrough = userContext.getResources().getBoolean(
+        final boolean tapThrough = userContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_navBarTapThrough);
-        if (navBarTapThrough) {
+        if (tapThrough) {
             tappableElementProvider.setInsetsSize(Insets.NONE);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 5d598e8..94f01b8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -74,7 +74,7 @@
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.rotation.FloatingRotationButton;
 import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.rotation.RotationButtonController;
@@ -149,7 +149,7 @@
     private NavigationBarInflaterView mNavigationInflaterView;
     private Optional<Recents> mRecentsOptional = Optional.empty();
     @Nullable
-    private NotificationPanelViewController mPanelView;
+    private ShadeViewController mPanelView;
     private RotationContextButton mRotationContextButton;
     private FloatingRotationButton mFloatingRotationButton;
     private RotationButtonController mRotationButtonController;
@@ -346,7 +346,8 @@
         mRecentsOptional = recentsOptional;
     }
 
-    public void setComponents(NotificationPanelViewController panel) {
+    /** */
+    public void setComponents(ShadeViewController panel) {
         mPanelView = panel;
         updatePanelSystemUiStateFlags();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 5f4e7cac..aab898e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -30,6 +30,7 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.ShortcutManager
+import android.graphics.drawable.Icon
 import android.os.Build
 import android.os.UserHandle
 import android.os.UserManager
@@ -84,12 +85,12 @@
     fun onBubbleExpandChanged(isExpanding: Boolean, key: String?) {
         if (!isEnabled) return
 
-        if (key != Bubble.KEY_APP_BUBBLE) return
+        val info = infoReference.getAndSet(null) ?: return
 
-        val info = infoReference.getAndSet(null)
+        if (key != Bubble.getAppBubbleKeyForApp(info.packageName, info.user)) return
 
         // Safe guard mechanism, this callback should only be called for app bubbles.
-        if (info?.launchMode != NoteTaskLaunchMode.AppBubble) return
+        if (info.launchMode != NoteTaskLaunchMode.AppBubble) return
 
         if (isExpanding) {
             logDebug { "onBubbleExpandChanged - expanding: $info" }
@@ -172,7 +173,7 @@
             return
         }
 
-        val info = resolver.resolveInfo(entryPoint, isKeyguardLocked)
+        val info = resolver.resolveInfo(entryPoint, isKeyguardLocked, user)
 
         if (info == null) {
             logDebug { "Default notes app isn't set" }
@@ -187,9 +188,10 @@
             logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" }
             when (info.launchMode) {
                 is NoteTaskLaunchMode.AppBubble -> {
-                    // TODO: provide app bubble icon
                     val intent = createNoteTaskIntent(info)
-                    bubbles.showOrHideAppBubble(intent, user, null /* icon */)
+                    val icon =
+                        Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+                    bubbles.showOrHideAppBubble(intent, user, icon)
                     // App bubble logging happens on `onBubbleExpandChanged`.
                     logDebug { "onShowNoteTask - opened as app bubble: $info" }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 2b9f0af..a758347 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -15,10 +15,13 @@
  */
 package com.android.systemui.notetask
 
+import android.os.UserHandle
+
 /** Contextual information required to launch a Note Task by [NoteTaskController]. */
 data class NoteTaskInfo(
     val packageName: String,
     val uid: Int,
+    val user: UserHandle,
     val entryPoint: NoteTaskEntryPoint? = null,
     val isKeyguardLocked: Boolean = false,
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index 616f9b5..89a8526 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -25,7 +25,6 @@
 import android.os.UserHandle
 import android.util.Log
 import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
-import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 
 class NoteTaskInfoResolver
@@ -33,15 +32,13 @@
 constructor(
     private val roleManager: RoleManager,
     private val packageManager: PackageManager,
-    private val userTracker: UserTracker,
 ) {
 
     fun resolveInfo(
         entryPoint: NoteTaskEntryPoint? = null,
         isKeyguardLocked: Boolean = false,
+        user: UserHandle,
     ): NoteTaskInfo? {
-        val user = userTracker.userHandle
-
         val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
 
         if (packageName.isNullOrEmpty()) return null
@@ -49,6 +46,7 @@
         return NoteTaskInfo(
             packageName = packageName,
             uid = packageManager.getUidOf(packageName, user),
+            user = user,
             entryPoint = entryPoint,
             isKeyguardLocked = isKeyguardLocked,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
new file mode 100644
index 0000000..253560b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.qs
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.fragments.FragmentService
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import javax.inject.Provider
+
+@SysUISingleton
+class QSFragmentStartable
+@Inject
+constructor(
+    private val fragmentService: FragmentService,
+    private val qsFragmentProvider: Provider<QSFragment>
+) : CoreStartable {
+    override fun start() {
+        fragmentService.addFragmentInstantiationProvider(QSFragment::class.java, qsFragmentProvider)
+    }
+}
+
+@Module
+interface QSFragmentStartableModule {
+    @Binds
+    @IntoMap
+    @ClassKey(QSFragmentStartable::class)
+    fun bindsQSFragmentStartable(startable: QSFragmentStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 0a188e0..a43f520 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -99,7 +99,7 @@
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -205,7 +205,7 @@
                 // TODO move this logic to message queue
                 mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
                     if (event.getActionMasked() == ACTION_DOWN) {
-                        centralSurfaces.getNotificationPanelViewController()
+                        centralSurfaces.getShadeViewController()
                                         .startExpandLatencyTracking();
                     }
                     mHandler.post(() -> {
@@ -676,9 +676,9 @@
                 mNavBarControllerLazy.get().getDefaultNavigationBar();
         final NavigationBarView navBarView =
                 mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
-        final NotificationPanelViewController panelController =
+        final ShadeViewController panelController =
                 mCentralSurfacesOptionalLazy.get()
-                        .map(CentralSurfaces::getNotificationPanelViewController)
+                        .map(CentralSurfaces::getShadeViewController)
                         .orElse(null);
         if (SysUiState.DEBUG) {
             Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index a4a7d4c..9e204e4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -178,7 +178,6 @@
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -243,11 +242,6 @@
 public final class NotificationPanelViewController implements ShadeSurface, Dumpable {
 
     public static final String TAG = NotificationPanelView.class.getSimpleName();
-    public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
-    public static final float FLING_SPEED_UP_FACTOR = 0.6f;
-    public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f;
-    public static final float FLING_CLOSING_SPEED_UP_FACTOR = 0.6f;
-    public static final int WAKEUP_ANIMATION_DELAY_MS = 250;
     private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     private static final boolean DEBUG_DRAWABLE = false;
@@ -255,12 +249,6 @@
             VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false);
     /** The parallax amount of the quick settings translation when dragging down the panel. */
     public static final float QS_PARALLAX_AMOUNT = 0.175f;
-    /** Fling expanding QS. */
-    public static final int FLING_EXPAND = 0;
-    /** Fling collapsing QS, potentially stopping when QS becomes QQS. */
-    public static final int FLING_COLLAPSE = 1;
-    /** Fling until QS is completely hidden. */
-    public static final int FLING_HIDE = 2;
     /** The delay to reset the hint text when the hint animation is finished running. */
     private static final int HINT_RESET_DELAY_MS = 1200;
     private static final long ANIMATION_DELAY_ICON_FADE_IN =
@@ -870,8 +858,8 @@
         mKeyguardBypassController = bypassController;
         mUpdateMonitor = keyguardUpdateMonitor;
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
-        lockscreenShadeTransitionController.setNotificationPanelController(this);
-        shadeTransitionController.setNotificationPanelViewController(this);
+        lockscreenShadeTransitionController.setShadeViewController(this);
+        shadeTransitionController.setShadeViewController(this);
         dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
         quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
         quickSettingsController.setQsStateUpdateListener(this::onQsStateUpdated);
@@ -1023,7 +1011,7 @@
         mKeyguardStatusBarViewController =
                 mKeyguardStatusBarViewComponentFactory.build(
                                 mKeyguardStatusBar,
-                                mNotificationPanelViewStateProvider)
+                                mShadeViewStateProvider)
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
 
@@ -1618,10 +1606,7 @@
         return isOnAod();
     }
 
-    /**
-     * Notify us that {@link NotificationWakeUpCoordinator} is going to play the doze wakeup
-     * animation after a delay. If so, we'll keep the clock centered until that animation starts.
-     */
+    @Override
     public void setWillPlayDelayedDozeAmountAnimation(boolean willPlay) {
         if (mWillPlayDelayedDozeAmountAnimation == willPlay) return;
 
@@ -1709,8 +1694,7 @@
         final float extraSpaceForShelf = lockIconPadding - noShelfOverlapBottomPadding;
 
         if (extraSpaceForShelf > 0f) {
-            return Math.min(mNotificationShelfController.getIntrinsicHeight(),
-                    extraSpaceForShelf);
+            return Math.min(getShelfHeight(), extraSpaceForShelf);
         }
         return 0f;
     }
@@ -1732,10 +1716,18 @@
                 mNotificationStackScrollLayoutController.getView(),
                 getVerticalSpaceForLockscreenNotifications(),
                 getVerticalSpaceForLockscreenShelf(),
-                mNotificationShelfController.getIntrinsicHeight()
+                getShelfHeight()
         );
     }
 
+    private int getShelfHeight() {
+        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+            return mNotificationStackScrollLayoutController.getShelfHeight();
+        } else {
+            return mNotificationShelfController.getIntrinsicHeight();
+        }
+    }
+
     private void updateClock() {
         if (mIsOcclusionTransitionRunning) {
             return;
@@ -3308,16 +3300,6 @@
         public boolean hasPulsingNotifications() {
             return mNotificationListContainer.hasPulsingNotifications();
         }
-
-        @Override
-        public ActivatableNotificationView getActivatedChild() {
-            return mNotificationStackScrollLayoutController.getActivatedChild();
-        }
-
-        @Override
-        public void setActivatedChild(ActivatableNotificationView o) {
-            mNotificationStackScrollLayoutController.setActivatedChild(o);
-        }
     }
 
     @Override
@@ -3338,12 +3320,12 @@
 
         mGestureRecorder = recorder;
         mHideExpandedRunnable = hideExpandedRunnable;
+        mNotificationShelfController = notificationShelfController;
         if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
             mNotificationStackScrollLayoutController.setShelfController(
                     notificationShelfController);
+            mLockscreenShadeTransitionController.bindController(notificationShelfController);
         }
-        mNotificationShelfController = notificationShelfController;
-        mLockscreenShadeTransitionController.bindController(notificationShelfController);
         updateMaxDisplayedNotifications(true);
     }
 
@@ -4384,29 +4366,8 @@
         }
     }
 
-    /**
-     * An interface that provides the current state of the notification panel and related views,
-     * which is needed to calculate {@link KeyguardStatusBarView}'s state in
-     * {@link KeyguardStatusBarViewController}.
-     */
-    public interface NotificationPanelViewStateProvider {
-        /** Returns the expanded height of the panel view. */
-        float getPanelViewExpandedHeight();
-
-        /**
-         * Returns true if heads up should be visible.
-         *
-         * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into
-         * {@link KeyguardStatusBarViewController} and remove this method.
-         */
-        boolean shouldHeadsUpBeVisible();
-
-        /** Return the fraction of the shade that's expanded, when in lockscreen. */
-        float getLockscreenShadeDragProgress();
-    }
-
-    private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider =
-            new NotificationPanelViewStateProvider() {
+    private final ShadeViewStateProvider mShadeViewStateProvider =
+            new ShadeViewStateProvider() {
                 @Override
                 public float getPanelViewExpandedHeight() {
                     return getExpandedHeight();
@@ -4423,13 +4384,7 @@
                 }
             };
 
-    /**
-     * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
-     * screen off animation controller in order to animate in AOD without "actually" fully switching
-     * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
-     * change.
-     */
-    @VisibleForTesting
+    @Override
     public void showAodUi() {
         setDozing(true /* dozing */, false /* animate */);
         mStatusBarStateController.setUpcomingState(KEYGUARD);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 2f4cc14..ebbf1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -288,7 +288,6 @@
                             mService.userActivity();
                             mService.wakeUpIfDozing(
                                     mClock.uptimeMillis(),
-                                    mView,
                                     "LOCK_ICON_TOUCH",
                                     PowerManager.WAKE_REASON_GESTURE);
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index b42bdaa..fd82e2f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -90,7 +90,6 @@
                 shadeLogger.d("Single tap handled, requesting centralSurfaces.wakeUpIfDozing")
                 centralSurfaces.wakeUpIfDozing(
                     SystemClock.uptimeMillis(),
-                    notificationShadeWindowView,
                     "PULSING_SINGLE_TAP",
                     PowerManager.WAKE_REASON_TAP
                 )
@@ -116,7 +115,6 @@
         ) {
             centralSurfaces.wakeUpIfDozing(
                     SystemClock.uptimeMillis(),
-                    notificationShadeWindowView,
                     "PULSING_DOUBLE_TAP",
                     PowerManager.WAKE_REASON_TAP
             )
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index a931838..1839e13 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -20,9 +20,9 @@
 import com.android.systemui.log.dagger.ShadeLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE
-import com.android.systemui.shade.NotificationPanelViewController.FLING_EXPAND
-import com.android.systemui.shade.NotificationPanelViewController.FLING_HIDE
+import com.android.systemui.shade.ShadeViewController.Companion.FLING_COLLAPSE
+import com.android.systemui.shade.ShadeViewController.Companion.FLING_EXPAND
+import com.android.systemui.shade.ShadeViewController.Companion.FLING_HIDE
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index b698bd3..5ac36bf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -94,6 +94,12 @@
     fun setTouchAndAnimationDisabled(disabled: Boolean)
 
     /**
+     * Notify us that {@link NotificationWakeUpCoordinator} is going to play the doze wakeup
+     * animation after a delay. If so, we'll keep the clock centered until that animation starts.
+     */
+    fun setWillPlayDelayedDozeAmountAnimation(willPlay: Boolean)
+
+    /**
      * Sets the dozing state.
      *
      * @param dozing `true` when dozing.
@@ -104,18 +110,12 @@
     /** @see view.setImportantForAccessibility */
     fun setImportantForAccessibility(mode: Int)
 
-    /** Sets Qs ScrimEnabled and updates QS state. */
-    fun setQsScrimEnabled(qsScrimEnabled: Boolean)
-
     /** Sets the view's X translation to zero. */
     fun resetTranslation()
 
     /** Sets the view's alpha to max. */
     fun resetAlpha()
 
-    /** @see ViewGroupFadeHelper.reset */
-    fun resetViewGroupFade()
-
     /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */
     fun onBackPressed()
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 34c9f6d..d5a9e95 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -18,11 +18,12 @@
 import android.view.MotionEvent
 import android.view.ViewGroup
 import com.android.systemui.statusbar.RemoteInputController
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView
+import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController
 import java.util.function.Consumer
 
 /**
@@ -148,6 +149,9 @@
     /** Sets whether the screen has temporarily woken up to display notifications. */
     fun setPulsing(pulsing: Boolean)
 
+    /** Sets Qs ScrimEnabled and updates QS state. */
+    fun setQsScrimEnabled(qsScrimEnabled: Boolean)
+
     /** Sets the top spacing for the ambient indicator. */
     fun setAmbientIndicationTop(ambientIndicationTop: Int, ambientTextVisible: Boolean)
 
@@ -167,6 +171,9 @@
      */
     val isUnlockHintRunning: Boolean
 
+    /** @see ViewGroupFadeHelper.reset */
+    fun resetViewGroupFade()
+
     /**
      * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
      * but not in shade locked / shade. This is used when dragging down to the full shade.
@@ -184,6 +191,14 @@
     fun setKeyguardStatusBarAlpha(alpha: Float)
 
     /**
+     * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
+     * screen off animation controller in order to animate in AOD without "actually" fully switching
+     * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
+     * change.
+     */
+    fun showAodUi()
+
+    /**
      * This method should not be used anymore, you should probably use [.isShadeFullyOpen] instead.
      * It was overused as indicating if shade is open or we're on keyguard/AOD. Moving forward we
      * should be explicit about the what state we're checking.
@@ -209,6 +224,23 @@
 
     /** Returns the ShadeNotificationPresenter. */
     val shadeNotificationPresenter: ShadeNotificationPresenter
+
+    companion object {
+        const val WAKEUP_ANIMATION_DELAY_MS = 250
+        const val FLING_MAX_LENGTH_SECONDS = 0.6f
+        const val FLING_SPEED_UP_FACTOR = 0.6f
+        const val FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f
+        const val FLING_CLOSING_SPEED_UP_FACTOR = 0.6f
+
+        /** Fling expanding QS. */
+        const val FLING_EXPAND = 0
+
+        /** Fling collapsing QS, potentially stopping when QS becomes QQS. */
+        const val FLING_COLLAPSE = 1
+
+        /** Fling until QS is completely hidden. */
+        const val FLING_HIDE = 2
+    }
 }
 
 /** Manages listeners for when users begin expanding the shade from a HUN. */
@@ -254,7 +286,24 @@
 
     /** Returns whether the screen has temporarily woken up to display notifications. */
     fun hasPulsingNotifications(): Boolean
+}
 
-    /** The current activated notification. */
-    var activatedChild: ActivatableNotificationView?
+/**
+ * An interface that provides the current state of the notification panel and related views, which
+ * is needed to calculate [KeyguardStatusBarView]'s state in [KeyguardStatusBarViewController].
+ */
+interface ShadeViewStateProvider {
+    /** Returns the expanded height of the panel view. */
+    val panelViewExpandedHeight: Float
+
+    /**
+     * Returns true if heads up should be visible.
+     *
+     * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into
+     *   [KeyguardStatusBarViewController] and remove this method.
+     */
+    fun shouldHeadsUpBeVisible(): Boolean
+
+    /** Return the fraction of the shade that's expanded, when in lockscreen. */
+    val lockscreenShadeDragProgress: Float
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
index 129d09e..41be526 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
@@ -22,10 +22,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.qs.QS
-import com.android.systemui.shade.NotificationPanelViewController
 import com.android.systemui.shade.PanelState
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.panelStateToString
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -47,7 +47,7 @@
     private val statusBarStateController: SysuiStatusBarStateController,
 ) {
 
-    lateinit var notificationPanelViewController: NotificationPanelViewController
+    lateinit var shadeViewController: ShadeViewController
     lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController
     lateinit var qs: QS
 
@@ -93,7 +93,7 @@
                 currentPanelState: ${currentPanelState?.panelStateToString()}
                 lastPanelExpansionChangeEvent: $lastShadeExpansionChangeEvent
                 qs.isInitialized: ${this::qs.isInitialized}
-                npvc.isInitialized: ${this::notificationPanelViewController.isInitialized}
+                npvc.isInitialized: ${this::shadeViewController.isInitialized}
                 nssl.isInitialized: ${this::notificationStackScrollLayoutController.isInitialized}
             """.trimIndent())
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index f7d37e6..4ef2063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -20,7 +20,6 @@
 
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -107,11 +106,6 @@
     }
 
     @Override
-    public void setOnActivatedListener(ActivatableNotificationView.OnActivatedListener listener) {
-        mView.setOnActivatedListener(listener);
-    }
-
-    @Override
     public void setOnClickListener(View.OnClickListener onClickListener) {
         mView.setOnClickListener(onClickListener);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 5fb5002..fec6112 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -6,7 +6,7 @@
 import com.android.systemui.R
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -17,7 +17,7 @@
 @AssistedInject
 constructor(
     private val mediaHierarchyManager: MediaHierarchyManager,
-    @Assisted private val notificationPanelController: NotificationPanelViewController,
+    @Assisted private val notificationPanelController: ShadeViewController,
     context: Context,
     configurationController: ConfigurationController,
     dumpManager: DumpManager
@@ -114,7 +114,7 @@
     @AssistedFactory
     fun interface Factory {
         fun create(
-            notificationPanelController: NotificationPanelViewController
+            notificationPanelController: ShadeViewController
         ): LockscreenShadeKeyguardTransitionController
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 63e29d1..faf592e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -30,7 +30,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.shade.NotificationPanelViewController
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -79,7 +79,7 @@
         private set
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
-    lateinit var notificationPanelController: NotificationPanelViewController
+    lateinit var shadeViewController: ShadeViewController
     lateinit var centralSurfaces: CentralSurfaces
     lateinit var qS: QS
 
@@ -182,7 +182,7 @@
     }
 
     private val keyguardTransitionController by lazy {
-        keyguardTransitionControllerFactory.create(notificationPanelController)
+        keyguardTransitionControllerFactory.create(shadeViewController)
     }
 
     private val qsTransitionController = qsTransitionControllerFactory.create { qS }
@@ -276,7 +276,6 @@
             if (statusBarStateController.state == StatusBarState.KEYGUARD) {
                 centralSurfaces.wakeUpIfDozing(
                         SystemClock.uptimeMillis(),
-                        it,
                         "SHADE_CLICK",
                         PowerManager.WAKE_REASON_GESTURE,
                 )
@@ -320,7 +319,7 @@
                             startingChild.onExpandedByGesture(
                                     true /* drag down is always an open */)
                         }
-                        notificationPanelController.transitionToExpandedShade(delay)
+                        shadeViewController.transitionToExpandedShade(delay)
                         callbacks.forEach { it.setTransitionToFullShadeAmount(0f,
                                 true /* animated */, delay) }
 
@@ -531,7 +530,7 @@
             } else {
                 // Let's only animate notifications
                 animationHandler = { delay: Long ->
-                    notificationPanelController.transitionToExpandedShade(delay)
+                    shadeViewController.transitionToExpandedShade(delay)
                 }
             }
             goToLockedShadeInternal(expandedView, animationHandler,
@@ -649,7 +648,7 @@
      */
     private fun performDefaultGoToFullShadeAnimation(delay: Long) {
         logger.logDefaultGoToFullShadeAnimation(delay)
-        notificationPanelController.transitionToExpandedShade(delay)
+        shadeViewController.transitionToExpandedShade(delay)
         animateAppear(delay)
     }
 
@@ -674,7 +673,7 @@
         } else {
             pulseHeight = height
             val overflow = nsslController.setPulseHeight(height)
-            notificationPanelController.setOverStretchAmount(overflow)
+            shadeViewController.setOverStretchAmount(overflow)
             val transitionHeight = if (keyguardBypassController.bypassEnabled) height else 0.0f
             transitionToShadeAmountCommon(transitionHeight)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index ea9817c..1714f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
@@ -43,6 +44,7 @@
 import android.view.View;
 import android.widget.ImageView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -69,6 +71,8 @@
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -78,8 +82,6 @@
 import java.util.Optional;
 import java.util.Set;
 
-import dagger.Lazy;
-
 /**
  * Handles tasks and state related to media notifications. For example, there is a 'current' media
  * notification, which this class keeps track of.
@@ -117,6 +119,8 @@
     private ScrimController mScrimController;
     @Nullable
     private LockscreenWallpaper mLockscreenWallpaper;
+    @VisibleForTesting
+    boolean mIsLockscreenLiveWallpaperEnabled;
 
     private final DelayableExecutor mMainExecutor;
 
@@ -157,12 +161,49 @@
                 Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
             }
             mMediaArtworkProcessor.clearCache();
-            mMediaMetadata = metadata;
+            mMediaMetadata = cleanMetadata(metadata);
             dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
         }
     };
 
     /**
+     * If this build is not configured for lockscreen artwork, clear artwork references from the
+     * metadata to avoid excess memory usage. Otherwise, return as is.
+     * @param data Original metadata
+     * @return a copy without artwork data, or original
+     */
+    private MediaMetadata cleanMetadata(MediaMetadata data) {
+        if (SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
+            return data;
+        }
+        if (data == null) {
+            return null;
+        }
+        if (DEBUG_MEDIA) {
+            String[] artKeys = new String[] {
+                MediaMetadata.METADATA_KEY_ART,
+                MediaMetadata.METADATA_KEY_ALBUM_ART,
+                MediaMetadata.METADATA_KEY_DISPLAY_ICON,
+                MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
+                MediaMetadata.METADATA_KEY_ART_URI,
+                MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
+            };
+            Log.v(TAG, "DEBUG_MEDIA: removing artwork from metadata");
+            for (String key: artKeys) {
+                Log.v(TAG, "  " + key + ": " + data.containsKey(key));
+            }
+        }
+        return new MediaMetadata.Builder(data)
+                .putBitmap(MediaMetadata.METADATA_KEY_ART, null)
+                .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null)
+                .putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, null)
+                .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, null)
+                .putString(MediaMetadata.METADATA_KEY_ART_URI, null)
+                .putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, null)
+                .build();
+    }
+
+    /**
      * Injected constructor. See {@link CentralSurfacesModule}.
      */
     public NotificationMediaManager(
@@ -179,7 +220,8 @@
             StatusBarStateController statusBarStateController,
             SysuiColorExtractor colorExtractor,
             KeyguardStateController keyguardStateController,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            WallpaperManager wallpaperManager) {
         mContext = context;
         mMediaArtworkProcessor = mediaArtworkProcessor;
         mKeyguardBypassController = keyguardBypassController;
@@ -195,6 +237,7 @@
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
         mKeyguardStateController = keyguardStateController;
+        mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled();
 
         setupNotifPipeline();
 
@@ -307,6 +350,7 @@
         return mMediaNotificationKey;
     }
 
+    @VisibleForTesting
     public MediaMetadata getMediaMetadata() {
         return mMediaMetadata;
     }
@@ -344,7 +388,7 @@
      * update this manager's internal state.
      * @return whether the current MediaMetadata changed (and needs to be announced to listeners).
      */
-    boolean findPlayingMediaNotification(
+    private boolean findPlayingMediaNotification(
             @NonNull Collection<NotificationEntry> allNotifications) {
         boolean metaDataChanged = false;
         // Promote the media notification with a controller in 'playing' state, if any.
@@ -377,7 +421,7 @@
             clearCurrentMediaNotificationSession();
             mMediaController = controller;
             mMediaController.registerCallback(mMediaListener);
-            mMediaMetadata = mMediaController.getMetadata();
+            mMediaMetadata = cleanMetadata(mMediaController.getMetadata());
             if (DEBUG_MEDIA) {
                 Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: "
                         + mMediaController + ", receive metadata: " + mMediaMetadata);
@@ -474,13 +518,16 @@
      * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
      */
     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+
+        if (mIsLockscreenLiveWallpaperEnabled) return;
+
         Trace.beginSection("CentralSurfaces#updateMediaMetaData");
         if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
             Trace.endSection();
             return;
         }
 
-        if (mBackdrop == null) {
+        if (getBackDropView() == null) {
             Trace.endSection();
             return; // called too early
         }
@@ -709,6 +756,12 @@
                 && (mBackdropFront.isVisibleToUser() || mBackdropBack.isVisibleToUser());
     }
 
+    // TODO(b/273443374) temporary test helper; remove
+    @VisibleForTesting
+    BackDropView getBackDropView() {
+        return mBackdrop;
+    }
+
     /**
      * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index cb414ba..1c9bc60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar;
 
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -25,8 +24,7 @@
  * for affecting the state of the system (e.g. starting an intent, given that the presenter may
  * want to perform some action before doing so).
  */
-public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
-        ActivatableNotificationView.OnActivatedListener {
+public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener {
     /**
      * Returns true if the presenter is not visible. For example, it may not be necessary to do
      * animations if this returns true.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9e2a07e..c519115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -124,7 +124,7 @@
                 View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
             mCentralSurfacesOptionalLazy.get().ifPresent(
                     centralSurfaces -> centralSurfaces.wakeUpIfDozing(
-                            SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK",
+                            SystemClock.uptimeMillis(), "NOTIFICATION_CLICK",
                             PowerManager.WAKE_REASON_GESTURE));
 
             final NotificationEntry entry = getNotificationForParent(view.getParent());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index 07cfd0d..1619dda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -21,8 +21,6 @@
 import android.view.View.OnClickListener
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView.OnActivatedListener
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -43,9 +41,6 @@
     /** Whether or not the shelf can modify the color of notifications in the shade. */
     fun canModifyColorOfNotifications(): Boolean
 
-    /** @see ActivatableNotificationView.setOnActivatedListener */
-    fun setOnActivatedListener(listener: OnActivatedListener)
-
     /** Binds the shelf to the host [NotificationStackScrollLayout], via its Controller. */
     fun bind(
         ambientState: AmbientState,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 34300c7..f6c9a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.dagger;
 
 import android.app.IActivityManager;
+import android.app.WallpaperManager;
 import android.content.Context;
 import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
@@ -142,7 +143,8 @@
             StatusBarStateController statusBarStateController,
             SysuiColorExtractor colorExtractor,
             KeyguardStateController keyguardStateController,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            WallpaperManager wallpaperManager) {
         return new NotificationMediaManager(
                 context,
                 centralSurfacesOptionalLazy,
@@ -157,7 +159,8 @@
                 statusBarStateController,
                 colorExtractor,
                 keyguardStateController,
-                dumpManager);
+                dumpManager,
+                wallpaperManager);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 00d8c42..5f28ecb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -31,26 +31,12 @@
     fun isDevLoggingEnabled(): Boolean =
         featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
 
-    fun fullScreenIntentRequiresKeyguard(): Boolean =
-        featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD)
-
-    fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE)
-
-    fun forceDemoteFsi(): Boolean =
-            sysPropFlags.isEnabled(NotificationFlags.FSI_FORCE_DEMOTE)
-
-    fun showStickyHunForDeniedFsi(): Boolean =
-            sysPropFlags.isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI)
-
     fun allowDismissOngoing(): Boolean =
             sysPropFlags.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)
 
     fun isOtpRedactionEnabled(): Boolean =
             sysPropFlags.isEnabled(NotificationFlags.OTP_REDACTION)
 
-    val shouldFilterUnseenNotifsOnKeyguard: Boolean
-        get() = featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
-
     val isNoHunForOldWhenEnabled: Boolean
         get() = featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 705cf92..bab553e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -71,7 +71,7 @@
         }
 
         mCentralSurfacesOptional.ifPresent(centralSurfaces -> centralSurfaces.wakeUpIfDozing(
-                SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK",
+                SystemClock.uptimeMillis(), "NOTIFICATION_CLICK",
                 PowerManager.WAKE_REASON_GESTURE));
 
         final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 20af6ca..fe0b28d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.NotificationPanelViewController.WAKEUP_ANIMATION_DELAY_MS
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -383,7 +383,7 @@
             ObjectAnimator.ofFloat(this, delayedDozeAmount, 0.0f).apply {
                 interpolator = InterpolatorsAndroidX.LINEAR
                 duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
-                startDelay = WAKEUP_ANIMATION_DELAY_MS.toLong()
+                startDelay = ShadeViewController.WAKEUP_ANIMATION_DELAY_MS.toLong()
                 doOnStart {
                     wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(true) }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 4d0e746..0529c94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -392,8 +392,7 @@
             mNotificationInterruptStateProvider.logFullScreenIntentDecision(entry, fsiDecision)
             if (fsiDecision.shouldLaunch) {
                 mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
-            } else if (mFlags.fsiOnDNDUpdate() &&
-                fsiDecision == FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND) {
+            } else if (fsiDecision == FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND) {
                 // If DND was the only reason this entry was suppressed, note it for potential
                 // reconsideration on later ranking updates.
                 addForFSIReconsideration(entry, mSystemClock.currentTimeMillis())
@@ -509,7 +508,7 @@
                 //   - was suppressed from FSI launch only by a DND suppression
                 //   - is within the recency window for reconsideration
                 // If any of these entries are no longer suppressed, launch the FSI now.
-                if (mFlags.fsiOnDNDUpdate() && isCandidateForFSIReconsideration(entry)) {
+                if (isCandidateForFSIReconsideration(entry)) {
                     val decision =
                         mNotificationInterruptStateProvider.getFullScreenIntentDecision(entry)
                     if (decision.shouldLaunch) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 6322edf..4cbbefe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -95,9 +95,7 @@
         pipeline.addFinalizeFilter(notifFilter)
         keyguardNotificationVisibilityProvider.addOnStateChangedListener(::invalidateListFromFilter)
         updateSectionHeadersVisibility()
-        if (notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard) {
-            attachUnseenFilter(pipeline)
-        }
+        attachUnseenFilter(pipeline)
     }
 
     private fun attachUnseenFilter(pipeline: NotifPipeline) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 611edf8..38bbb35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection.inflation;
 
-import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -31,7 +30,6 @@
 
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -73,7 +71,6 @@
     private NotificationListContainer mListContainer;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
-    private FeatureFlags mFeatureFlags;
 
     @Inject
     public NotificationRowBinderImpl(
@@ -85,8 +82,7 @@
             RowContentBindStage rowContentBindStage,
             Provider<RowInflaterTask> rowInflaterTaskProvider,
             ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
-            IconManager iconManager,
-            FeatureFlags featureFlags) {
+            IconManager iconManager) {
         mContext = context;
         mNotifBindPipeline = notifBindPipeline;
         mRowContentBindStage = rowContentBindStage;
@@ -96,7 +92,6 @@
         mRowInflaterTaskProvider = rowInflaterTaskProvider;
         mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
         mIconManager = iconManager;
-        mFeatureFlags = featureFlags;
     }
 
     /**
@@ -177,12 +172,9 @@
     private void bindRow(NotificationEntry entry, ExpandableNotificationRow row) {
         mListContainer.bindRow(row);
         mNotificationRemoteInputManager.bindRow(row);
-        row.setOnActivatedListener(mPresenter);
         entry.setRow(row);
         mNotifBindPipeline.manageRow(entry, row);
         mBindRowCallback.onBindRow(row);
-        row.setInlineReplyAnimationFlagEnabled(
-                mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 9a1747a..88994b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -98,10 +98,6 @@
          */
         NO_FSI_NO_HUN_OR_KEYGUARD(false),
         /**
-         * No conditions blocking FSI launch.
-         */
-        FSI_EXPECTED_NOT_TO_HUN(true),
-        /**
          * The notification is coming from a suspended packages, so FSI is suppressed.
          */
         NO_FSI_SUSPENDED(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index a48870b..609f9d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -321,30 +321,22 @@
                     suppressedByDND);
         }
 
-        // Check whether FSI requires the keyguard to be showing.
-        if (mFlags.fullScreenIntentRequiresKeyguard()) {
-
-            // If notification won't HUN and keyguard is showing, launch the FSI.
-            if (mKeyguardStateController.isShowing()) {
-                if (mKeyguardStateController.isOccluded()) {
-                    return getDecisionGivenSuppression(
-                            FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED,
-                            suppressedByDND);
-                } else {
-                    // Likely LOCKED_SHADE, but launch FSI anyway
-                    return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_LOCKED_SHADE,
-                            suppressedByDND);
-                }
+        // If notification won't HUN and keyguard is showing, launch the FSI.
+        if (mKeyguardStateController.isShowing()) {
+            if (mKeyguardStateController.isOccluded()) {
+                return getDecisionGivenSuppression(
+                        FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED,
+                        suppressedByDND);
+            } else {
+                // Likely LOCKED_SHADE, but launch FSI anyway
+                return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_LOCKED_SHADE,
+                        suppressedByDND);
             }
-
-            // Detect the case determined by b/231322873 to launch FSI while device is in use,
-            // as blocked by the correct implementation, and report the event.
-            return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
-                    suppressedByDND);
         }
 
-        // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI.
-        return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN,
+        // Detect the case determined by b/231322873 to launch FSI while device is in use,
+        // as blocked by the correct implementation, and report the event.
+        return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
                 suppressedByDND);
     }
 
@@ -409,14 +401,11 @@
         }
 
         final boolean isSnoozedPackage = isSnoozedPackage(sbn);
-        final boolean fsiRequiresKeyguard = mFlags.fullScreenIntentRequiresKeyguard();
         final boolean hasFsi = sbn.getNotification().fullScreenIntent != null;
 
         // Assume any notification with an FSI is time-sensitive (like an alarm or incoming call)
         // and ignore whether HUNs have been snoozed for the package.
-        final boolean shouldBypassSnooze = fsiRequiresKeyguard && hasFsi;
-
-        if (isSnoozedPackage && !shouldBypassSnooze) {
+        if (isSnoozedPackage && !hasFsi) {
             if (log) mLogger.logNoHeadsUpPackageSnoozed(entry);
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 68ad49be..766ad88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -28,7 +28,6 @@
 import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
@@ -57,30 +56,6 @@
 public abstract class ActivatableNotificationView extends ExpandableOutlineView {
 
     /**
-     * The amount of width, which is kept in the end when performing a disappear animation (also
-     * the amount from which the horizontal appearing begins)
-     */
-    private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
-
-    /**
-     * At which point from [0,1] does the horizontal collapse animation end (or start when
-     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
-     */
-    private static final float HORIZONTAL_ANIMATION_END = 0.2f;
-
-    /**
-     * At which point from [0,1] does the horizontal collapse animation start (or start when
-     * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
-     */
-    private static final float HORIZONTAL_ANIMATION_START = 1.0f;
-
-    /**
-     * At which point from [0,1] does the vertical collapse animation start (or end when
-     * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
-     */
-    private static final float VERTICAL_ANIMATION_START = 1.0f;
-
-    /**
      * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)}
      * or {@link #setOverrideTintColor(int, float)}.
      */
@@ -95,10 +70,6 @@
      * The start of the animation is at #ALPHA_APPEAR_START_FRACTION
      */
     private static final float ALPHA_APPEAR_END_FRACTION = 1;
-    private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
-            = new PathInterpolator(0.6f, 0, 0.5f, 1);
-    private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
-            = new PathInterpolator(0, 0, 0.5f, 1);
     private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>();
     private int mTintedRippleColor;
     private int mNormalRippleColor;
@@ -112,10 +83,7 @@
      */
     private boolean mActivated;
 
-    private OnActivatedListener mOnActivatedListener;
-
     private final Interpolator mSlowOutFastInInterpolator;
-    private final Interpolator mSlowOutLinearInInterpolator;
     private Interpolator mCurrentAppearInterpolator;
 
     NotificationBackgroundView mBackgroundNormal;
@@ -142,13 +110,11 @@
     protected Point mTargetPoint;
     private boolean mDismissed;
     private boolean mRefocusOnDismiss;
-    private AccessibilityManager mAccessibilityManager;
     protected boolean mUseRoundnessSourceTypes;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
-        mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
         setClipChildren(false);
         setClipToPadding(false);
         updateColors();
@@ -230,7 +196,7 @@
     public void onTap() {}
 
     /** Sets the last action up time this view was touched. */
-    void setLastActionUpTime(long eventTime) {
+    public void setLastActionUpTime(long eventTime) {
         mLastActionUpTime = eventTime;
     }
 
@@ -249,10 +215,6 @@
         return false;
     }
 
-    protected boolean handleSlideBack() {
-        return false;
-    }
-
     /**
      * @return whether this view is interactive and can be double tapped
      */
@@ -270,40 +232,12 @@
         mBackgroundNormal.setPressedAllowed(allowed);
     }
 
-    void makeActive() {
-        mActivated = true;
-        if (mOnActivatedListener != null) {
-            mOnActivatedListener.onActivated(this);
-        }
-    }
-
-    public boolean isActive() {
-        return mActivated;
-    }
-
-    /**
-     * Cancels the hotspot and makes the notification inactive.
-     */
-    public void makeInactive(boolean animate) {
-        if (mActivated) {
-            mActivated = false;
-        }
-        if (mOnActivatedListener != null) {
-            mOnActivatedListener.onActivationReset(this);
-        }
-    }
-
     private void updateOutlineAlpha() {
         float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
         alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
         setOutlineAlpha(alpha);
     }
 
-    private void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
-        mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
-        updateOutlineAlpha();
-    }
-
     @Override
     public void setBelowSpeedBump(boolean below) {
         super.setBelowSpeedBump(below);
@@ -318,13 +252,6 @@
     }
 
     /**
-     * @return whether we are below the speed bump
-     */
-    public boolean isBelowSpeedBump() {
-        return mIsBelowSpeedBump;
-    }
-
-    /**
      * Sets the tint color of the background
      */
     protected void setTintColor(int color) {
@@ -728,10 +655,6 @@
         }
     }
 
-    public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
-        mOnActivatedListener = onActivatedListener;
-    }
-
     @Override
     public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
             int outlineTranslation) {
@@ -782,14 +705,10 @@
         return mRefocusOnDismiss || isAccessibilityFocused();
     }
 
-    void setTouchHandler(Gefingerpoken touchHandler) {
+    public void setTouchHandler(Gefingerpoken touchHandler) {
         mTouchHandler = touchHandler;
     }
 
-    public void setAccessibilityManager(AccessibilityManager accessibilityManager) {
-        mAccessibilityManager = accessibilityManager;
-    }
-
     /**
      * Enable the support for rounded corner based on the SourceType
      * @param enabled true if is supported
@@ -832,9 +751,4 @@
             });
         }
     }
-
-    public interface OnActivatedListener {
-        void onActivated(ActivatableNotificationView view);
-        void onActivationReset(ActivatableNotificationView view);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index b084a76..028cd18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -16,15 +16,12 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import android.os.SystemClock;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.phone.NotificationTapHelper;
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
@@ -37,44 +34,16 @@
     private final ExpandableOutlineViewController mExpandableOutlineViewController;
     private final AccessibilityManager mAccessibilityManager;
     private final FalsingManager mFalsingManager;
-    private final FalsingCollector mFalsingCollector;
-    private final NotificationTapHelper mNotificationTapHelper;
     private final TouchHandler mTouchHandler = new TouchHandler();
 
-    private boolean mNeedsDimming;
-
     @Inject
     public ActivatableNotificationViewController(ActivatableNotificationView view,
-            NotificationTapHelper.Factory notificationTapHelpFactory,
             ExpandableOutlineViewController expandableOutlineViewController,
-            AccessibilityManager accessibilityManager, FalsingManager falsingManager,
-            FalsingCollector falsingCollector) {
+            AccessibilityManager accessibilityManager, FalsingManager falsingManager) {
         super(view);
         mExpandableOutlineViewController = expandableOutlineViewController;
         mAccessibilityManager = accessibilityManager;
         mFalsingManager = falsingManager;
-        mFalsingCollector = falsingCollector;
-
-        mNotificationTapHelper = notificationTapHelpFactory.create(
-                (active) -> {
-                    if (active) {
-                        mView.makeActive();
-                        mFalsingCollector.onNotificationActive();
-                    } else {
-                        mView.makeInactive(true /* animate */);
-                    }
-                }, mView::performClick, mView::handleSlideBack);
-
-        mView.setOnActivatedListener(new ActivatableNotificationView.OnActivatedListener() {
-            @Override
-            public void onActivated(ActivatableNotificationView view) {
-                mFalsingCollector.onNotificationActive();
-            }
-
-            @Override
-            public void onActivationReset(ActivatableNotificationView view) {
-            }
-        });
     }
 
     /**
@@ -85,7 +54,6 @@
         mExpandableOutlineViewController.init();
         mView.setOnTouchListener(mTouchHandler);
         mView.setTouchHandler(mTouchHandler);
-        mView.setAccessibilityManager(mAccessibilityManager);
     }
 
     @Override
@@ -99,15 +67,9 @@
     }
 
     class TouchHandler implements Gefingerpoken, View.OnTouchListener {
-        private boolean mBlockNextTouch;
-
         @Override
         public boolean onTouch(View v, MotionEvent ev) {
             boolean result = false;
-            if (mBlockNextTouch) {
-                mBlockNextTouch = false;
-                return true;
-            }
             if (ev.getAction() == MotionEvent.ACTION_UP) {
                 mView.setLastActionUpTime(ev.getEventTime());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 950ab5d..10cdae3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -272,7 +272,6 @@
     private OnExpandClickListener mOnExpandClickListener;
     private View.OnClickListener mOnFeedbackClickListener;
     private Path mExpandingClipPath;
-    private boolean mIsInlineReplyAnimationFlagEnabled = false;
 
     // Listener will be called when receiving a long click event.
     // Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -969,15 +968,6 @@
     }
 
     @Override
-    protected boolean handleSlideBack() {
-        if (mMenuRow != null && mMenuRow.isMenuVisible()) {
-            animateResetTranslation();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
     public boolean isSummaryWithChildren() {
         return mIsSummaryWithChildren;
     }
@@ -3095,10 +3085,6 @@
         return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
     }
 
-    public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) {
-        mIsInlineReplyAnimationFlagEnabled = isEnabled;
-    }
-
     @Override
     public void setActualHeight(int height, boolean notifyListeners) {
         boolean changed = height != getActualHeight();
@@ -3118,11 +3104,7 @@
         }
         int contentHeight = Math.max(getMinHeight(), height);
         for (NotificationContentView l : mLayouts) {
-            if (mIsInlineReplyAnimationFlagEnabled) {
-                l.setContentHeight(height);
-            } else {
-                l.setContentHeight(contentHeight);
-            }
+            l.setContentHeight(height);
         }
         if (mIsSummaryWithChildren) {
             mChildrenContainer.setActualHeight(height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 5ca0866..9bf05cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -376,8 +376,7 @@
     public boolean offerToKeepInParentForAnimation() {
         //If the User dismissed the notification's parent, we want to keep it attached until the
         //dismiss animation is ongoing. Therefore we don't want to remove it in the ShadeViewDiffer.
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)
-                && mView.isParentDismissed()) {
+        if (mView.isParentDismissed()) {
             mView.setKeepInParentForDismissAnimation(true);
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 451d837..f432af2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -635,8 +635,7 @@
         int hint;
         if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
             hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
-            if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isAnimatingAppearance()
-                    && mHeadsUpRemoteInputController.isFocusAnimationFlagActive()) {
+            if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isAnimatingAppearance()) {
                 // While the RemoteInputView is animating its appearance, it should be allowed
                 // to overlap the hint, therefore no space is reserved for the hint during the
                 // appearance animation of the RemoteInputView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
new file mode 100644
index 0000000..54af107
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.statusbar.notification.row.ui.viewbinder
+
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.OnTouchListener
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Gefingerpoken
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
+
+/** Binds an [ActivatableNotificationView] to its [view model][ActivatableNotificationViewModel]. */
+object ActivatableNotificationViewBinder {
+
+    fun bind(
+        viewModel: ActivatableNotificationViewModel,
+        view: ActivatableNotificationView,
+        falsingManager: FalsingManager,
+    ) {
+        ExpandableOutlineViewBinder.bind(viewModel, view)
+        val touchHandler = TouchHandler(view, falsingManager)
+        view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.isTouchable.collect { isTouchable ->
+                        touchHandler.isTouchEnabled = isTouchable
+                    }
+                }
+                view.registerListenersWhileAttached(touchHandler)
+            }
+        }
+    }
+
+    private suspend fun ActivatableNotificationView.registerListenersWhileAttached(
+        touchHandler: TouchHandler,
+    ): Unit =
+        try {
+            setOnTouchListener(touchHandler)
+            setTouchHandler(touchHandler)
+            awaitCancellation()
+        } finally {
+            setTouchHandler(null)
+            setOnTouchListener(null)
+        }
+}
+
+private class TouchHandler(
+    private val view: ActivatableNotificationView,
+    private val falsingManager: FalsingManager,
+) : Gefingerpoken, OnTouchListener {
+
+    var isTouchEnabled = false
+
+    override fun onTouch(v: View, ev: MotionEvent): Boolean {
+        val result = false
+        if (ev.action == MotionEvent.ACTION_UP) {
+            view.setLastActionUpTime(ev.eventTime)
+        }
+        // With a11y, just do nothing.
+        if (!isTouchEnabled) {
+            return false
+        }
+        if (ev.action == MotionEvent.ACTION_UP) {
+            // If this is a false tap, capture the even so it doesn't result in a click.
+            val falseTap: Boolean = falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
+            if (!falseTap && v is ActivatableNotificationView) {
+                v.onTap()
+            }
+            return falseTap
+        }
+        return result
+    }
+
+    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean = false
+
+    /** Use [onTouch] instead. */
+    override fun onTouchEvent(ev: MotionEvent): Boolean = false
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableOutlineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableOutlineViewBinder.kt
new file mode 100644
index 0000000..745ce77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableOutlineViewBinder.kt
@@ -0,0 +1,13 @@
+package com.android.systemui.statusbar.notification.row.ui.viewbinder
+
+import com.android.systemui.statusbar.notification.row.ExpandableOutlineView
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ExpandableOutlineViewModel as ViewModel
+
+object ExpandableOutlineViewBinder {
+    fun bind(
+        viewModel: ViewModel,
+        view: ExpandableOutlineView,
+    ) {
+        ExpandableViewBinder.bind(viewModel, view)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableViewBinder.kt
new file mode 100644
index 0000000..49cfb576
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableViewBinder.kt
@@ -0,0 +1,8 @@
+package com.android.systemui.statusbar.notification.row.ui.viewbinder
+
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ExpandableViewModel as ViewModel
+
+object ExpandableViewBinder {
+    fun bind(viewModel: ViewModel, view: ExpandableView) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
new file mode 100644
index 0000000..f46d424
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.statusbar.notification.row.ui.viewmodel
+
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** ViewModel for [com.android.systemui.statusbar.notification.row.ActivatableNotificationView]. */
+interface ActivatableNotificationViewModel : ExpandableOutlineViewModel {
+    /** Does the view react to touches? */
+    val isTouchable: Flow<Boolean>
+
+    companion object {
+        operator fun invoke(
+            a11yInteractor: AccessibilityInteractor,
+        ): ActivatableNotificationViewModel = ActivatableNotificationViewModelImpl(a11yInteractor)
+    }
+}
+
+private class ActivatableNotificationViewModelImpl(
+    a11yInteractor: AccessibilityInteractor,
+) : ActivatableNotificationViewModel {
+    override val isTouchable: Flow<Boolean> =
+        // If a11y touch exploration is enabled, then the activatable view should ignore touches
+        a11yInteractor.isTouchExplorationEnabled.map { !it }
+}
+
+@Module
+object ActivatableNotificationViewModelModule {
+    @Provides
+    fun provideViewModel(interactor: AccessibilityInteractor) =
+        ActivatableNotificationViewModel(interactor)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableOutlineViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableOutlineViewModel.kt
new file mode 100644
index 0000000..5904c77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableOutlineViewModel.kt
@@ -0,0 +1,4 @@
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+/** ViewModel for [com.android.systemui.statusbar.notification.row.ExpandableOutlineView]. */
+interface ExpandableOutlineViewModel : ExpandableViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableViewModel.kt
new file mode 100644
index 0000000..5efaf04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableViewModel.kt
@@ -0,0 +1,4 @@
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+/** ViewModel for [com.android.systemui.statusbar.notification.row.ExpandableView]. */
+interface ExpandableViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 8ba65f7..014406f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -16,10 +16,14 @@
 
 package com.android.systemui.statusbar.notification.shelf.domain.interactor
 
+import android.os.PowerManager
 import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -31,6 +35,9 @@
 constructor(
     private val keyguardRepository: KeyguardRepository,
     private val deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository,
+    private val centralSurfaces: CentralSurfaces,
+    private val systemClock: SystemClock,
+    private val keyguardTransitionController: LockscreenShadeTransitionController,
 ) {
     /** Is the shelf showing on the keyguard? */
     val isShowingOnKeyguard: Flow<Boolean>
@@ -45,4 +52,14 @@
             ) { isKeyguardShowing, isBypassEnabled ->
                 isKeyguardShowing && isBypassEnabled
             }
+
+    /** Transition keyguard to the locked shade, triggered by the shelf. */
+    fun goToLockedShadeFromShelf() {
+        centralSurfaces.wakeUpIfDozing(
+            systemClock.uptimeMillis(),
+            "SHADE_CLICK",
+            PowerManager.WAKE_REASON_GESTURE,
+        )
+        keyguardTransitionController.goToLockedShade(null)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index b190cf6..c823189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -17,10 +17,8 @@
 package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
 
 import android.view.View
-import android.view.accessibility.AccessibilityManager
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -28,19 +26,17 @@
 import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.NotificationShelfController
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController
-import com.android.systemui.statusbar.notification.row.ExpandableOutlineViewController
-import com.android.systemui.statusbar.notification.row.ExpandableViewController
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
 import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.NotificationTapHelper
 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
 import com.android.systemui.util.kotlin.getValue
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 
@@ -57,11 +53,9 @@
     private val shelf: NotificationShelf,
     private val viewModel: NotificationShelfViewModel,
     featureFlags: FeatureFlags,
-    private val notifTapHelperFactory: NotificationTapHelper.Factory,
-    private val a11yManager: AccessibilityManager,
     private val falsingManager: FalsingManager,
-    private val falsingCollector: FalsingCollector,
     hostControllerLazy: Lazy<NotificationStackScrollLayoutController>,
+    private val notificationIconAreaController: NotificationIconAreaController,
 ) : NotificationShelfController {
 
     private val hostController: NotificationStackScrollLayoutController by hostControllerLazy
@@ -78,43 +72,28 @@
     }
 
     fun init() {
-        NotificationShelfViewBinder.bind(viewModel, shelf)
-
-        ActivatableNotificationViewController(
-                shelf,
-                notifTapHelperFactory,
-                ExpandableOutlineViewController(shelf, ExpandableViewController(shelf)),
-                a11yManager,
-                falsingManager,
-                falsingCollector,
-            )
-            .init()
+        NotificationShelfViewBinder.bind(viewModel, shelf, falsingManager)
         hostController.setShelf(shelf)
         hostController.setOnNotificationRemovedListener { child, _ ->
             view.requestRoundnessResetFor(child)
         }
+        notificationIconAreaController.setShelfIcons(shelf.shelfIcons)
     }
 
     override val intrinsicHeight: Int
-        get() = shelf.intrinsicHeight
+        get() = unsupported
 
     override val shelfIcons: NotificationIconContainer
-        get() = shelf.shelfIcons
+        get() = unsupported
 
     override fun canModifyColorOfNotifications(): Boolean = unsupported
 
-    override fun setOnActivatedListener(listener: ActivatableNotificationView.OnActivatedListener) {
-        shelf.setOnActivatedListener(listener)
-    }
-
     override fun bind(
         ambientState: AmbientState,
         notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
     ) = unsupported
 
-    override fun setOnClickListener(listener: View.OnClickListener) {
-        shelf.setOnClickListener(listener)
-    }
+    override fun setOnClickListener(listener: View.OnClickListener) = unsupported
 
     private val unsupported: Nothing
         get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
@@ -122,14 +101,32 @@
 
 /** Binds a [NotificationShelf] to its backend. */
 object NotificationShelfViewBinder {
-    fun bind(viewModel: NotificationShelfViewModel, shelf: NotificationShelf) {
+    fun bind(
+        viewModel: NotificationShelfViewModel,
+        shelf: NotificationShelf,
+        falsingManager: FalsingManager,
+    ) {
+        ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
         shelf.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 viewModel.canModifyColorOfNotifications
                     .onEach(shelf::setCanModifyColorOfNotifications)
                     .launchIn(this)
                 viewModel.isClickable.onEach(shelf::setCanInteract).launchIn(this)
+                registerViewListenersWhileAttached(shelf, viewModel)
             }
         }
     }
+
+    private suspend fun registerViewListenersWhileAttached(
+        shelf: NotificationShelf,
+        viewModel: NotificationShelfViewModel,
+    ) {
+        try {
+            shelf.setOnClickListener { viewModel.onShelfClicked() }
+            awaitCancellation()
+        } finally {
+            shelf.setOnClickListener(null)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index 5e297c8..fb19443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.shelf.ui.viewmodel
 
 import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
 import javax.inject.Inject
@@ -29,7 +30,8 @@
 @Inject
 constructor(
     private val interactor: NotificationShelfInteractor,
-) {
+    activatableViewModel: ActivatableNotificationViewModel,
+) : ActivatableNotificationViewModel by activatableViewModel {
     /** Is the shelf allowed to be clickable when it has content? */
     val isClickable: Flow<Boolean>
         get() = interactor.isShowingOnKeyguard
@@ -37,4 +39,9 @@
     /** Is the shelf allowed to modify the color of notifications in the host layout? */
     val canModifyColorOfNotifications: Flow<Boolean>
         get() = interactor.isShelfStatic.map { static -> !static }
+
+    /** Notifies that the user has clicked the shelf. */
+    fun onShelfClicked() {
+        interactor.goToLockedShadeFromShelf()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 77ede04..ae7c216 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -34,7 +34,6 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController;
@@ -65,7 +64,6 @@
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private int mScrollY;
     private boolean mDimmed;
-    private ActivatableNotificationView mActivatedChild;
     private float mOverScrollTopAmount;
     private float mOverScrollBottomAmount;
     private boolean mDozing;
@@ -360,14 +358,6 @@
         mHideSensitive = hideSensitive;
     }
 
-    /**
-     * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
-     * interaction. This child is then scaled normally and its background is fully opaque.
-     */
-    public void setActivatedChild(ActivatableNotificationView activatedChild) {
-        mActivatedChild = activatedChild;
-    }
-
     public boolean isDimmed() {
         // While we are expanding from pulse, we want the notifications not to be dimmed, otherwise
         // you'd see the difference to the pulsing notification
@@ -382,10 +372,6 @@
         return mHideSensitive;
     }
 
-    public ActivatableNotificationView getActivatedChild() {
-        return mActivatedChild;
-    }
-
     public void setOverScrollAmount(float amount, boolean onTop) {
         if (onTop) {
             mOverScrollTopAmount = amount;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index af608a7..555d502 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4469,24 +4469,6 @@
         }
     }
 
-    /**
-     * See {@link AmbientState#setActivatedChild}.
-     */
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void setActivatedChild(ActivatableNotificationView activatedChild) {
-        mAmbientState.setActivatedChild(activatedChild);
-        if (mAnimationsEnabled) {
-            mActivateNeedsAnimation = true;
-            mNeedsAnimation = true;
-        }
-        requestChildrenUpdate();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public ActivatableNotificationView getActivatedChild() {
-        return mAmbientState.getActivatedChild();
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void applyCurrentState() {
         int numChildren = getChildCount();
@@ -5137,6 +5119,15 @@
         requestChildrenUpdate();
     }
 
+    @Nullable
+    public ExpandableView getShelf() {
+        if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) {
+            return mShelf;
+        } else {
+            return null;
+        }
+    }
+
     public void setShelf(NotificationShelf shelf) {
         if (!NotificationShelfController.checkRefactorFlagEnabled(
                 mAmbientState.getFeatureFlags())) {
@@ -5236,7 +5227,6 @@
     void onStatePostChange(boolean fromShadeLocked) {
         boolean onKeyguard = onKeyguard();
 
-        mAmbientState.setActivatedChild(null);
         mAmbientState.setDimmed(onKeyguard);
 
         if (mHeadsUpAppearanceController != null) {
@@ -5245,11 +5235,6 @@
 
         setDimmed(onKeyguard, fromShadeLocked);
         setExpandingEnabled(!onKeyguard);
-        ActivatableNotificationView activatedChild = getActivatedChild();
-        setActivatedChild(null);
-        if (activatedChild != null) {
-            activatedChild.makeInactive(false /* animate */);
-        }
         updateFooter();
         requestChildrenUpdate();
         onUpdateRowStates();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 1c8727f..e2f93ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -180,7 +180,6 @@
     private final SeenNotificationsProvider mSeenNotificationsProvider;
 
     private NotificationStackScrollLayout mView;
-    private boolean mFadeNotificationsOnDismiss;
     private NotificationSwipeHelper mSwipeHelper;
     @Nullable
     private Boolean mHistoryEnabled;
@@ -548,7 +547,7 @@
                 public boolean updateSwipeProgress(View animView, boolean dismissable,
                                                    float swipeProgress) {
                     // Returning true prevents alpha fading.
-                    return !mFadeNotificationsOnDismiss;
+                    return false;
                 }
 
                 @Override
@@ -773,7 +772,6 @@
 
         mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
 
-        mFadeNotificationsOnDismiss = mFeatureFlags.isEnabled(Flags.NOTIFICATION_DISMISSAL_FADE);
         if (!mUseRoundnessSourceTypes) {
             mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
             mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
@@ -1370,14 +1368,6 @@
         mView.onUpdateRowStates();
     }
 
-    public ActivatableNotificationView getActivatedChild() {
-        return mView.getActivatedChild();
-    }
-
-    public void setActivatedChild(ActivatableNotificationView view) {
-        mView.setActivatedChild(view);
-    }
-
     public void runAfterAnimationFinished(Runnable r) {
         mView.runAfterAnimationFinished(r);
     }
@@ -1600,6 +1590,14 @@
         mView.setShelf(shelf);
     }
 
+    public int getShelfHeight() {
+        if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+            return 0;
+        }
+        ExpandableView shelf = mView.getShelf();
+        return shelf == null ? 0 : shelf.getIntrinsicHeight();
+    }
+
     /**
      * Enum for UiEvent logged from this class
      */
@@ -1942,8 +1940,7 @@
         public void setNotifStats(@NonNull NotifStats notifStats) {
             mNotifStats = notifStats;
             mView.setHasFilteredOutSeenNotifications(
-                    mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
-                            && mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
+                    mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
             updateFooter();
             updateShowEmptyShadeView();
         }
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 b1fb13e0..3060473 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
@@ -126,7 +126,7 @@
         updateHeadsUpStates(algorithmState, ambientState);
         updatePulsingStates(algorithmState, ambientState);
 
-        updateDimmedActivatedHideSensitive(ambientState, algorithmState);
+        updateDimmedAndHideSensitive(ambientState, algorithmState);
         updateClipping(algorithmState, ambientState);
         updateSpeedBumpState(algorithmState, speedBumpIndex);
         updateShelfState(algorithmState, ambientState);
@@ -341,25 +341,17 @@
         }
     }
 
-    /**
-     * Updates the dimmed, activated and hiding sensitive states of the children.
-     */
-    private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
-                                                    StackScrollAlgorithmState algorithmState) {
+    /** Updates the dimmed and hiding sensitive states of the children. */
+    private void updateDimmedAndHideSensitive(AmbientState ambientState,
+            StackScrollAlgorithmState algorithmState) {
         boolean dimmed = ambientState.isDimmed();
         boolean hideSensitive = ambientState.isHideSensitive();
-        View activatedChild = ambientState.getActivatedChild();
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             ExpandableViewState childViewState = child.getViewState();
             childViewState.dimmed = dimmed;
             childViewState.hideSensitive = hideSensitive;
-            boolean isActivatedChild = activatedChild == child;
-            if (dimmed && isActivatedChild) {
-                childViewState.setZTranslation(childViewState.getZTranslation()
-                        + 2.0f * ambientState.getZDistanceBetweenElements());
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 3a1272f..b15d241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -48,9 +48,9 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.LightRevealScrim;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.util.Compile;
@@ -75,7 +75,7 @@
     boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
     // additional instrumentation for testing purposes; intended to be left on during development
     boolean CHATTY = DEBUG;
-    boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
+    boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = false;
     String ACTION_FAKE_ARTWORK = "fake_artwork";
     int FADE_KEYGUARD_START_DELAY = 100;
     int FADE_KEYGUARD_DURATION = 300;
@@ -212,13 +212,14 @@
     /**
      * Wakes up the device if the device was dozing.
      */
-    void wakeUpIfDozing(long time, View where, String why, @PowerManager.WakeReason int wakeReason);
+    void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason);
 
     NotificationShadeWindowView getNotificationShadeWindowView();
 
     NotificationShadeWindowViewController getNotificationShadeWindowViewController();
 
-    NotificationPanelViewController getNotificationPanelViewController();
+    /** */
+    ShadeViewController getShadeViewController();
 
     /** Get the Keyguard Message Area that displays auth messages. */
     AuthKeyguardMessageArea getKeyguardMessageArea();
@@ -430,9 +431,6 @@
 
     void collapseShade();
 
-    /** Collapse the shade, but conditional on a flag specific to the trigger of a bugreport. */
-    void collapseShadeForBugreport();
-
     int getWakefulnessState();
 
     boolean isScreenFullyOff();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 8b6617b..c0a7a34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -54,9 +54,9 @@
 import com.android.systemui.qs.QSPanelController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
@@ -80,7 +80,7 @@
     private final Context mContext;
     private final com.android.systemui.shade.ShadeController mShadeController;
     private final CommandQueue mCommandQueue;
-    private final NotificationPanelViewController mNotificationPanelViewController;
+    private final ShadeViewController mShadeViewController;
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MetricsLogger mMetricsLogger;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -117,7 +117,7 @@
             @Main Resources resources,
             ShadeController shadeController,
             CommandQueue commandQueue,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             MetricsLogger metricsLogger,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -144,7 +144,7 @@
         mContext = context;
         mShadeController = shadeController;
         mCommandQueue = commandQueue;
-        mNotificationPanelViewController = notificationPanelViewController;
+        mShadeViewController = shadeViewController;
         mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
         mMetricsLogger = metricsLogger;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -218,7 +218,7 @@
             return;
         }
 
-        mNotificationPanelViewController.expandToNotifications();
+        mShadeViewController.expandToNotifications();
     }
 
     @Override
@@ -234,7 +234,7 @@
         // Settings are not available in setup
         if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
 
-        mNotificationPanelViewController.expandToQs();
+        mShadeViewController.expandToQs();
     }
 
     @Override
@@ -300,7 +300,7 @@
             }
         }
 
-        mNotificationPanelViewController.disableHeader(state1, state2, animate);
+        mShadeViewController.disableHeader(state1, state2, animate);
     }
 
     /**
@@ -322,22 +322,22 @@
 
         if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key.getKeyCode()) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
-            mNotificationPanelViewController.collapse(
+            mShadeViewController.collapse(
                     false /* delayed */, 1.0f /* speedUpFactor */);
         } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key.getKeyCode()) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
-            if (mNotificationPanelViewController.isFullyCollapsed()) {
+            if (mShadeViewController.isFullyCollapsed()) {
                 if (mVibrateOnOpening) {
                     mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
                 }
-                mNotificationPanelViewController.expand(true /* animate */);
+                mShadeViewController.expand(true /* animate */);
                 mNotificationStackScrollLayoutController.setWillExpand(true);
                 mHeadsUpManager.unpinAll(true /* userUnpinned */);
                 mMetricsLogger.count("panel_open", 1);
             } else if (!mQsController.getExpanded()
-                    && !mNotificationPanelViewController.isExpanding()) {
+                    && !mShadeViewController.isExpanding()) {
                 mQsController.flingQs(0 /* velocity */,
-                        NotificationPanelViewController.FLING_EXPAND);
+                        ShadeViewController.FLING_EXPAND);
                 mMetricsLogger.count("panel_open_qs", 1);
             }
         }
@@ -355,7 +355,7 @@
             return;
         }
         if (!mCameraLauncherLazy.get().canCameraGestureBeLaunched(
-                mNotificationPanelViewController.getBarState())) {
+                mShadeViewController.getBarState())) {
             if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
                 Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
             }
@@ -394,7 +394,7 @@
                     mStatusBarKeyguardViewManager.reset(true /* hide */);
                 }
                 mCameraLauncherLazy.get().launchCamera(source,
-                        mNotificationPanelViewController.isFullyCollapsed());
+                        mShadeViewController.isFullyCollapsed());
                 mCentralSurfaces.updateScrimController();
             } else {
                 // We need to defer the camera launch until the screen comes on, since otherwise
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 0960efb..b3953a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -193,6 +193,8 @@
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shade.ShadeLogger;
+import com.android.systemui.shade.ShadeSurface;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CircleReveal;
@@ -230,6 +232,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
 import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -505,7 +508,7 @@
 
     /** Controller for the Shade. */
     @VisibleForTesting
-    NotificationPanelViewController mNotificationPanelViewController;
+    ShadeSurface mShadeSurface;
     private final ShadeLogger mShadeLogger;
 
     // settings
@@ -698,9 +701,9 @@
         @Override
         public void onBackProgressed(BackEvent event) {
             if (shouldBackBeHandled()) {
-                if (mNotificationPanelViewController.canBeCollapsed()) {
+                if (mShadeSurface.canBeCollapsed()) {
                     float fraction = event.getProgress();
-                    mNotificationPanelViewController.onBackProgressed(fraction);
+                    mShadeSurface.onBackProgressed(fraction);
                 }
             }
         }
@@ -951,11 +954,6 @@
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mBubblesOptional.ifPresent(this::initBubbles);
 
-        // Do not restart System UI when the bugreport flag changes.
-        mFeatureFlags.addListener(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, event -> {
-            event.requestNoRestart();
-        });
-
         mStatusBarSignalPolicy.init();
         mKeyguardIndicationController.init();
 
@@ -1090,7 +1088,7 @@
                 this,
                 mStatusBarKeyguardViewManager,
                 mNotificationShadeWindowViewController,
-                mNotificationPanelViewController,
+                mShadeSurface,
                 mAmbientIndicationContainer);
         updateLightRevealScrimVisibility();
 
@@ -1253,7 +1251,9 @@
 
         // TODO: Deal with the ugliness that comes from having some of the status bar broken out
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
-        mNotificationIconAreaController.setupShelf(mNotificationShelfController);
+        if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+            mNotificationIconAreaController.setupShelf(mNotificationShelfController);
+        }
         mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
 
         // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1274,7 +1274,7 @@
                     // re-display the notification panel if necessary (for example, if
                     // a heads-up notification was being displayed and should continue being
                     // displayed).
-                    mNotificationPanelViewController.updateExpansionAndVisibility();
+                    mShadeSurface.updateExpansionAndVisibility();
                     setBouncerShowingForStatusBarComponents(mBouncerShowing);
                     checkBarModes();
                 });
@@ -1349,7 +1349,7 @@
         mScreenOffAnimationController.initialize(this, mLightRevealScrim);
         updateLightRevealScrimVisibility();
 
-        mNotificationPanelViewController.initDependencies(
+        mShadeSurface.initDependencies(
                 this,
                 mGestureRec,
                 mShadeController::makeExpandedInvisible,
@@ -1357,8 +1357,13 @@
                 mHeadsUpManager);
 
         BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
-        mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
-                backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
+        if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
+            mMediaManager.setup(null, null, null, mScrimController, null);
+        } else {
+            mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
+                    backdrop.findViewById(R.id.backdrop_back), mScrimController,
+                    mLockscreenWallpaper);
+        }
         float maxWallpaperZoom = mContext.getResources().getFloat(
                 com.android.internal.R.dimen.config_wallpaperMaxScale);
         mNotificationShadeDepthControllerLazy.get().addListener(depth -> {
@@ -1386,7 +1391,7 @@
                             .build());
             mBrightnessMirrorController = new BrightnessMirrorController(
                     mNotificationShadeWindowView,
-                    mNotificationPanelViewController,
+                    mShadeSurface,
                     mNotificationShadeDepthControllerLazy.get(),
                     mBrightnessSliderFactory,
                     (visible) -> {
@@ -1486,7 +1491,7 @@
                 || !mKeyguardStateController.canDismissLockScreen()
                 || mKeyguardViewMediator.isAnySimPinSecure()
                 || (mQsController.getExpanded() && trackingTouch)
-                || mNotificationPanelViewController.getBarState() == StatusBarState.SHADE_LOCKED) {
+                || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED) {
             return;
         }
 
@@ -1506,8 +1511,8 @@
         boolean tracking = event.getTracking();
         dispatchPanelExpansionForKeyguardDismiss(fraction, tracking);
 
-        if (getNotificationPanelViewController() != null) {
-            getNotificationPanelViewController().updateSystemUiStateFlags();
+        if (getShadeViewController() != null) {
+            getShadeViewController().updateSystemUiStateFlags();
         }
 
         if (fraction == 0 || fraction == 1) {
@@ -1563,7 +1568,6 @@
                 mNotifListContainer,
                 mHeadsUpManager,
                 mJankMonitor);
-        mNotificationShelfController.setOnActivatedListener(mPresenter);
         mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
         mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -1590,12 +1594,10 @@
      * Ask the display to wake up if currently dozing, else do nothing
      *
      * @param time when to wake up
-     * @param where the view requesting the wakeup
      * @param why the reason for the wake up
      */
     @Override
-    public void wakeUpIfDozing(long time, View where, String why,
-            @PowerManager.WakeReason int wakeReason) {
+    public void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason) {
         if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) {
             mPowerManager.wakeUp(
                     time, wakeReason, "com.android.systemui:" + why);
@@ -1631,16 +1633,19 @@
             }
         }
         mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
-        mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent);
+        mFragmentService.addFragmentInstantiationProvider(
+                CollapsedStatusBarFragment.class,
+                mCentralSurfacesComponent::createCollapsedStatusBarFragment);
 
         mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
         mNotificationShadeWindowViewController = mCentralSurfacesComponent
                 .getNotificationShadeWindowViewController();
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowViewController.setupExpandedStatusBar();
-        mNotificationPanelViewController =
+        NotificationPanelViewController npvc =
                 mCentralSurfacesComponent.getNotificationPanelViewController();
-        mShadeController.setNotificationPanelViewController(mNotificationPanelViewController);
+        mShadeSurface = npvc;
+        mShadeController.setNotificationPanelViewController(npvc);
         mShadeController.setNotificationShadeWindowViewController(
                 mNotificationShadeWindowViewController);
         mCentralSurfacesComponent.getLockIconViewController().init();
@@ -1706,7 +1711,7 @@
                 });
         mKeyguardViewMediator.registerCentralSurfaces(
                 /* statusBar= */ this,
-                mNotificationPanelViewController,
+                mShadeSurface,
                 mShadeExpansionStateManager,
                 mBiometricUnlockController,
                 mStackScroller,
@@ -1734,8 +1739,8 @@
     }
 
     @Override
-    public NotificationPanelViewController getNotificationPanelViewController() {
-        return mNotificationPanelViewController;
+    public ShadeViewController getShadeViewController() {
+        return mShadeSurface;
     }
 
     @Override
@@ -2092,16 +2097,16 @@
         }
 
         if (start) {
-            mNotificationPanelViewController.startWaitingForExpandGesture();
+            mShadeSurface.startWaitingForExpandGesture();
         } else {
-            mNotificationPanelViewController.stopWaitingForExpandGesture(cancel, velocity);
+            mShadeSurface.stopWaitingForExpandGesture(cancel, velocity);
         }
     }
 
     @Override
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mNotificationPanelViewController.collapse(
+            mShadeSurface.collapse(
                     true, false /* delayed */, 1.0f /* speedUpFactor */);
         }
     }
@@ -2726,7 +2731,7 @@
      */
     @Override
     public void setLockscreenUser(int newUserId) {
-        if (mLockscreenWallpaper != null) {
+        if (mLockscreenWallpaper != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
             mLockscreenWallpaper.setCurrentUser(newUserId);
         }
         mScrimController.setCurrentUser(newUserId);
@@ -2752,8 +2757,8 @@
             mStatusBarWindowController.refreshStatusBarHeight();
         }
 
-        if (mNotificationPanelViewController != null) {
-            mNotificationPanelViewController.updateResources();
+        if (mShadeSurface != null) {
+            mShadeSurface.updateResources();
         }
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.updateResources();
@@ -3011,7 +3016,7 @@
     public void showKeyguardImpl() {
         Trace.beginSection("CentralSurfaces#showKeyguard");
         if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
-            mNotificationPanelViewController.cancelAnimation();
+            mShadeSurface.cancelAnimation();
             onLaunchTransitionFadingEnded();
         }
         mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
@@ -3030,7 +3035,7 @@
     }
 
     private void onLaunchTransitionFadingEnded() {
-        mNotificationPanelViewController.resetAlpha();
+        mShadeSurface.resetAlpha();
         mCameraLauncherLazy.get().setLaunchingAffordance(false);
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
@@ -3060,8 +3065,8 @@
             }
             updateScrimController();
             mPresenter.updateMediaMetaData(false, true);
-            mNotificationPanelViewController.resetAlpha();
-            mNotificationPanelViewController.fadeOut(
+            mShadeSurface.resetAlpha();
+            mShadeSurface.fadeOut(
                     FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
                     this::onLaunchTransitionFadingEnded);
             mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
@@ -3093,7 +3098,7 @@
         Log.w(TAG, "Launch transition: Timeout!");
         mCameraLauncherLazy.get().setLaunchingAffordance(false);
         releaseGestureWakeLock();
-        mNotificationPanelViewController.resetViews(false /* animate */);
+        mShadeSurface.resetViews(false /* animate */);
     }
 
     private void runLaunchTransitionEndRunnable() {
@@ -3133,7 +3138,7 @@
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
             mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
-        } else if (!mNotificationPanelViewController.isCollapsing()) {
+        } else if (!mShadeSurface.isCollapsing()) {
             mShadeController.instantCollapseShade();
         }
 
@@ -3145,9 +3150,9 @@
         mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         releaseGestureWakeLock();
         mCameraLauncherLazy.get().setLaunchingAffordance(false);
-        mNotificationPanelViewController.resetAlpha();
-        mNotificationPanelViewController.resetTranslation();
-        mNotificationPanelViewController.resetViewGroupFade();
+        mShadeSurface.resetAlpha();
+        mShadeSurface.resetTranslation();
+        mShadeSurface.resetViewGroupFade();
         updateDozingState();
         updateScrimController();
         Trace.endSection();
@@ -3249,7 +3254,7 @@
         boolean animate = (!mDozing && shouldAnimateDozeWakeup())
                 || (mDozing && mDozeParameters.shouldControlScreenOff() && keyguardVisibleOrWillBe);
 
-        mNotificationPanelViewController.setDozing(mDozing, animate);
+        mShadeSurface.setDozing(mDozing, animate);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -3329,16 +3334,16 @@
             return true;
         }
         if (mQsController.getExpanded()) {
-            mNotificationPanelViewController.animateCollapseQs(false);
+            mShadeSurface.animateCollapseQs(false);
             return true;
         }
-        if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
+        if (mShadeSurface.closeUserSwitcherIfOpen()) {
             return true;
         }
         if (shouldBackBeHandled()) {
-            if (mNotificationPanelViewController.canBeCollapsed()) {
+            if (mShadeSurface.canBeCollapsed()) {
                 // this is the Shade dismiss animation, so make sure QQS closes when it ends.
-                mNotificationPanelViewController.onBackPressed();
+                mShadeSurface.onBackPressed();
                 mShadeController.animateCollapseShade();
             }
             return true;
@@ -3481,7 +3486,7 @@
         mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
         mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
         if (mBouncerShowing) {
-            wakeUpIfDozing(SystemClock.uptimeMillis(), null, "BOUNCER_VISIBLE",
+            wakeUpIfDozing(SystemClock.uptimeMillis(), "BOUNCER_VISIBLE",
                     PowerManager.WAKE_REASON_GESTURE);
         }
         updateScrimController();
@@ -3514,8 +3519,8 @@
         if (mPhoneStatusBarViewController != null) {
             mPhoneStatusBarViewController.setImportantForAccessibility(importance);
         }
-        mNotificationPanelViewController.setImportantForAccessibility(importance);
-        mNotificationPanelViewController.setBouncerShowing(bouncerShowing);
+        mShadeSurface.setImportantForAccessibility(importance);
+        mShadeSurface.setBouncerShowing(bouncerShowing);
     }
 
     /**
@@ -3523,7 +3528,7 @@
      */
     @Override
     public void collapseShade() {
-        if (mNotificationPanelViewController.isTracking()) {
+        if (mShadeSurface.isTracking()) {
             mNotificationShadeWindowViewController.cancelCurrentTouch();
         }
         if (mPanelExpanded && mState == StatusBarState.SHADE) {
@@ -3531,13 +3536,6 @@
         }
     }
 
-    @Override
-    public void collapseShadeForBugreport() {
-        if (!mFeatureFlags.isEnabled(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT)) {
-            collapseShade();
-        }
-    }
-
     @VisibleForTesting
     final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
         @Override
@@ -3634,7 +3632,7 @@
                     }
                 }
 
-                mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(
+                mShadeSurface.setWillPlayDelayedDozeAmountAnimation(
                         mShouldDelayWakeUpAnimation);
                 mWakeUpCoordinator.setWakingUp(
                         /* wakingUp= */ true,
@@ -3676,12 +3674,12 @@
                 // So if AOD is off or unsupported we need to trigger these updates at screen on
                 // when the keyguard is occluded.
                 mLockscreenUserManager.updatePublicMode();
-                mNotificationPanelViewController.getNotificationStackScrollLayoutController()
+                mShadeSurface.getNotificationStackScrollLayoutController()
                         .updateSensitivenessForOccludedWakeup();
             }
             if (mLaunchCameraWhenFinishedWaking) {
                 mCameraLauncherLazy.get().launchCamera(mLastCameraLaunchSource,
-                        mNotificationPanelViewController.isFullyCollapsed());
+                        mShadeSurface.isFullyCollapsed());
                 mLaunchCameraWhenFinishedWaking = false;
             }
             if (mLaunchEmergencyActionWhenFinishedWaking) {
@@ -3712,7 +3710,7 @@
                 !mDozeParameters.shouldControlScreenOff(), !mDeviceInteractive,
                 !mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive());
 
-        mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
+        mShadeSurface.setTouchAndAnimationDisabled(disabled);
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
 
@@ -3720,7 +3718,7 @@
         @Override
         public void onScreenTurningOn() {
             mFalsingCollector.onScreenTurningOn();
-            mNotificationPanelViewController.onScreenTurningOn();
+            mShadeSurface.onScreenTurningOn();
         }
 
         @Override
@@ -4364,7 +4362,7 @@
             }
             // We need the new R.id.keyguard_indication_area before recreating
             // mKeyguardIndicationController
-            mNotificationPanelViewController.onThemeChanged();
+            mShadeSurface.onThemeChanged();
 
             if (mStatusBarKeyguardViewManager != null) {
                 mStatusBarKeyguardViewManager.onThemeChanged();
@@ -4410,7 +4408,7 @@
                     mNavigationBarController.touchAutoDim(mDisplayId);
                     Trace.beginSection("CentralSurfaces#updateKeyguardState");
                     if (mState == StatusBarState.KEYGUARD) {
-                        mNotificationPanelViewController.cancelPendingCollapse();
+                        mShadeSurface.cancelPendingCollapse();
                     }
                     updateDozingState();
                     checkBarModes();
@@ -4436,7 +4434,7 @@
                             && mDozeParameters.shouldControlScreenOff();
                     // resetting views is already done when going into doze, there's no need to
                     // reset them again when we're waking up
-                    mNotificationPanelViewController.resetViews(dozingAnimated && isDozing);
+                    mShadeSurface.resetViews(dozingAnimated && isDozing);
 
                     updateQsExpansionEnabled();
                     mKeyguardViewMediator.setDozing(mDozing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 5196e10..89c3946 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -37,8 +37,8 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
@@ -50,12 +50,12 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.util.Assert;
 
+import dagger.Lazy;
+
 import java.util.ArrayList;
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Implementation of DozeHost for SystemUI.
  */
@@ -90,7 +90,7 @@
     private final AuthController mAuthController;
     private final NotificationIconAreaController mNotificationIconAreaController;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private NotificationPanelViewController mNotificationPanel;
+    private ShadeViewController mNotificationPanel;
     private View mAmbientIndicationContainer;
     private CentralSurfaces mCentralSurfaces;
     private boolean mAlwaysOnSuppressed;
@@ -141,7 +141,7 @@
             CentralSurfaces centralSurfaces,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             NotificationShadeWindowViewController notificationShadeWindowViewController,
-            NotificationPanelViewController notificationPanel,
+            ShadeViewController notificationPanel,
             View ambientIndicationContainer) {
         mCentralSurfaces = centralSurfaces;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 171e3d0..e705afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -31,8 +31,8 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeHeadsUpTracker;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
@@ -76,7 +76,7 @@
     private final NotificationStackScrollLayoutController mStackScrollerController;
 
     private final DarkIconDispatcher mDarkIconDispatcher;
-    private final NotificationPanelViewController mNotificationPanelViewController;
+    private final ShadeViewController mShadeViewController;
     private final NotificationRoundnessManager mNotificationRoundnessManager;
     private final boolean mUseRoundnessSourceTypes;
     private final Consumer<ExpandableNotificationRow>
@@ -118,7 +118,7 @@
             KeyguardStateController keyguardStateController,
             CommandQueue commandQueue,
             NotificationStackScrollLayoutController stackScrollerController,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             NotificationRoundnessManager notificationRoundnessManager,
             FeatureFlags featureFlags,
             HeadsUpStatusBarView headsUpStatusBarView,
@@ -134,13 +134,13 @@
         // has started pulling down the notification shade from the HUN and then the font size
         // changes). We need to re-fetch these values since they're used to correctly display the
         // HUN during this shade expansion.
-        mTrackedChild = notificationPanelViewController.getShadeHeadsUpTracker()
+        mTrackedChild = shadeViewController.getShadeHeadsUpTracker()
                 .getTrackedHeadsUpNotification();
         mAppearFraction = stackScrollerController.getAppearFraction();
         mExpandedHeight = stackScrollerController.getExpandedHeight();
 
         mStackScrollerController = stackScrollerController;
-        mNotificationPanelViewController = notificationPanelViewController;
+        mShadeViewController = shadeViewController;
         mStackScrollerController.setHeadsUpAppearanceController(this);
         mClockView = clockView;
         mOperatorNameViewOptional = operatorNameViewOptional;
@@ -179,7 +179,7 @@
     }
 
     private ShadeHeadsUpTracker getShadeHeadsUpTracker() {
-        return mNotificationPanelViewController.getShadeHeadsUpTracker();
+        return mShadeViewController.getShadeHeadsUpTracker();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index c163a89..90a6d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -27,7 +27,7 @@
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
 
 /**
@@ -84,7 +84,7 @@
     private int mSplitShadeTargetTopMargin;
 
     /**
-     * @see NotificationPanelViewController#getExpandedFraction()
+     * @see ShadeViewController#getExpandedFraction()
      */
     private float mPanelExpansion;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 2814e8d..e835c5ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -47,7 +47,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.log.LogLevel;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarState;
@@ -69,6 +69,8 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.SecureSettings;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -76,8 +78,6 @@
 
 import javax.inject.Inject;
 
-import kotlin.Unit;
-
 /** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
 public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
     private static final String TAG = "KeyguardStatusBarViewController";
@@ -104,8 +104,7 @@
     private final StatusBarIconController mStatusBarIconController;
     private final StatusBarIconController.TintedIconManager.Factory mTintedIconManagerFactory;
     private final BatteryMeterViewController mBatteryMeterViewController;
-    private final NotificationPanelViewController.NotificationPanelViewStateProvider
-            mNotificationPanelViewStateProvider;
+    private final ShadeViewStateProvider mShadeViewStateProvider;
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardBypassController mKeyguardBypassController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -274,8 +273,7 @@
             StatusBarIconController statusBarIconController,
             StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory,
             BatteryMeterViewController batteryMeterViewController,
-            NotificationPanelViewController.NotificationPanelViewStateProvider
-                    notificationPanelViewStateProvider,
+            ShadeViewStateProvider shadeViewStateProvider,
             KeyguardStateController keyguardStateController,
             KeyguardBypassController bypassController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -299,7 +297,7 @@
         mStatusBarIconController = statusBarIconController;
         mTintedIconManagerFactory = tintedIconManagerFactory;
         mBatteryMeterViewController = batteryMeterViewController;
-        mNotificationPanelViewStateProvider = notificationPanelViewStateProvider;
+        mShadeViewStateProvider = shadeViewStateProvider;
         mKeyguardStateController = keyguardStateController;
         mKeyguardBypassController = bypassController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -470,7 +468,7 @@
 
     /**
      * Updates the {@link KeyguardStatusBarView} state based on what the
-     * {@link NotificationPanelViewController.NotificationPanelViewStateProvider} and other
+     * {@link ShadeViewController.NotificationPanelViewStateProvider} and other
      * controllers provide.
      */
     public void updateViewState() {
@@ -479,7 +477,7 @@
         }
 
         float alphaQsExpansion = 1 - Math.min(
-                1, mNotificationPanelViewStateProvider.getLockscreenShadeDragProgress() * 2);
+                1, mShadeViewStateProvider.getLockscreenShadeDragProgress() * 2);
 
         float newAlpha;
         if (mExplicitAlpha != -1) {
@@ -530,12 +528,12 @@
         if (isKeyguardShowing()) {
             // When on Keyguard, we hide the header as soon as we expanded close enough to the
             // header
-            alpha = mNotificationPanelViewStateProvider.getPanelViewExpandedHeight()
+            alpha = mShadeViewStateProvider.getPanelViewExpandedHeight()
                     / (mView.getHeight() + mNotificationsHeaderCollideDistance);
         } else {
             // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
             // soon as we start translating the stack.
-            alpha = mNotificationPanelViewStateProvider.getPanelViewExpandedHeight()
+            alpha = mShadeViewStateProvider.getPanelViewExpandedHeight()
                     / mView.getHeight();
         }
         alpha = MathUtils.saturate(alpha);
@@ -585,7 +583,7 @@
 
     void updateForHeadsUp(boolean animate) {
         boolean showingKeyguardHeadsUp =
-                isKeyguardShowing() && mNotificationPanelViewStateProvider.shouldHeadsUpBeVisible();
+                isKeyguardShowing() && mShadeViewStateProvider.shouldHeadsUpBeVisible();
         if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
             mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
             if (isKeyguardShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index c16877a..c07b5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -63,6 +63,11 @@
 
     private static final String TAG = "LockscreenWallpaper";
 
+    // TODO(b/253507223): temporary; remove this
+    private static final String DISABLED_ERROR_MESSAGE = "Methods from LockscreenWallpaper.java "
+            + "should not be called in this version. The lock screen wallpaper should be "
+            + "managed by the WallpaperManagerService and not by this class.";
+
     private final NotificationMediaManager mMediaManager;
     private final WallpaperManager mWallpaperManager;
     private final KeyguardUpdateMonitor mUpdateMonitor;
@@ -91,7 +96,7 @@
         mMediaManager = mediaManager;
         mH = mainHandler;
 
-        if (iWallpaperManager != null) {
+        if (iWallpaperManager != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
             // Service is disabled on some devices like Automotive
             try {
                 iWallpaperManager.setLockWallpaperCallback(this);
@@ -102,6 +107,8 @@
     }
 
     public Bitmap getBitmap() {
+        assertLockscreenLiveWallpaperNotEnabled();
+
         if (mCached) {
             return mCache;
         }
@@ -122,9 +129,8 @@
     public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
         // May be called on any thread - only use thread safe operations.
 
-        if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
-            return LoaderResult.success(null);
-        }
+        assertLockscreenLiveWallpaperNotEnabled();
+
 
         if (!mWallpaperManager.isWallpaperSupported()) {
             // When wallpaper is not supported, show the system wallpaper
@@ -164,6 +170,8 @@
     }
 
     public void setCurrentUser(int user) {
+        assertLockscreenLiveWallpaperNotEnabled();
+
         if (user != mCurrentUserId) {
             if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
                 mCached = false;
@@ -173,6 +181,8 @@
     }
 
     public void setSelectedUser(UserHandle selectedUser) {
+        assertLockscreenLiveWallpaperNotEnabled();
+
         if (Objects.equals(selectedUser, mSelectedUser)) {
             return;
         }
@@ -182,16 +192,18 @@
 
     @Override
     public void onWallpaperChanged() {
+        assertLockscreenLiveWallpaperNotEnabled();
         // Called on Binder thread.
         postUpdateWallpaper();
     }
 
     @Override
     public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) {
-
+        assertLockscreenLiveWallpaperNotEnabled();
     }
 
     private void postUpdateWallpaper() {
+        assertLockscreenLiveWallpaperNotEnabled();
         if (mH == null) {
             Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization.");
             return;
@@ -199,11 +211,12 @@
         mH.removeCallbacks(this);
         mH.post(this);
     }
-
     @Override
     public void run() {
         // Called in response to onWallpaperChanged on the main thread.
 
+        assertLockscreenLiveWallpaperNotEnabled();
+
         if (mLoader != null) {
             mLoader.cancel(false /* interrupt */);
         }
@@ -358,4 +371,16 @@
             }
         }
     }
+
+    /**
+     * Feature b/253507223 will adapt the logic to always use the
+     * WallpaperManagerService to render the lock screen wallpaper.
+     * Methods of this class should not be called at all if the project flag is enabled.
+     * TODO(b/253507223) temporary assertion; remove this
+     */
+    private void assertLockscreenLiveWallpaperNotEnabled() {
+        if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
+            throw new IllegalStateException(DISABLED_ERROR_MESSAGE);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 057fa42..55dc188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -23,6 +23,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -92,6 +93,8 @@
 
     private final DemoModeController mDemoModeController;
 
+    private final FeatureFlags mFeatureFlags;
+
     private int mAodIconAppearTranslation;
 
     private boolean mAnimationsEnabled;
@@ -122,11 +125,12 @@
             Optional<Bubbles> bubblesOptional,
             DemoModeController demoModeController,
             DarkIconDispatcher darkIconDispatcher,
-            StatusBarWindowController statusBarWindowController,
+            FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController,
             ScreenOffAnimationController screenOffAnimationController) {
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
         mStatusBarStateController = statusBarStateController;
+        mFeatureFlags = featureFlags;
         mStatusBarStateController.addCallback(this);
         mMediaManager = notificationMediaManager;
         mDozeParameters = dozeParameters;
@@ -192,9 +196,16 @@
     }
 
     public void setupShelf(NotificationShelfController notificationShelfController) {
+        NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
         mShelfIcons = notificationShelfController.getShelfIcons();
     }
 
+    public void setShelfIcons(NotificationIconContainer icons) {
+        if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+            mShelfIcons = icons;
+        }
+    }
+
     public void onDensityOrFontScaleChanged(Context context) {
         updateIconLayoutParams(context);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 62d302f..e6cb68f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -165,19 +165,19 @@
             if (event.action == MotionEvent.ACTION_DOWN) {
                 // If the view that would receive the touch is disabled, just have status
                 // bar eat the gesture.
-                if (!centralSurfaces.notificationPanelViewController.isViewEnabled) {
+                if (!centralSurfaces.shadeViewController.isViewEnabled) {
                     shadeLogger.logMotionEvent(event,
                             "onTouchForwardedFromStatusBar: panel view disabled")
                     return true
                 }
-                if (centralSurfaces.notificationPanelViewController.isFullyCollapsed &&
+                if (centralSurfaces.shadeViewController.isFullyCollapsed &&
                         event.y < 1f) {
                     // b/235889526 Eat events on the top edge of the phone when collapsed
                     shadeLogger.logMotionEvent(event, "top edge touch ignored")
                     return true
                 }
             }
-            return centralSurfaces.notificationPanelViewController.handleExternalTouch(event)
+            return centralSurfaces.shadeViewController.handleExternalTouch(event)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index d3aa4bf..51c56a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -64,7 +64,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.scrim.ScrimView;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -649,7 +649,7 @@
         calculateAndUpdatePanelExpansion();
     }
 
-    /** See {@link NotificationPanelViewController#setPanelScrimMinFraction(float)}. */
+    /** See {@link ShadeViewController#setPanelScrimMinFraction(float)}. */
     public void setPanelScrimMinFraction(float minFraction) {
         if (isNaN(minFraction)) {
             throw new IllegalArgumentException("minFraction should not be NaN");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index a7413d5..481cf3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -34,7 +34,7 @@
 public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener {
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarWindowController mStatusBarWindowController;
-    private final NotificationPanelViewController mNotificationPanelViewController;
+    private final ShadeViewController mShadeViewController;
     private final KeyguardBypassController mKeyguardBypassController;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final StatusBarStateController mStatusBarStateController;
@@ -44,7 +44,7 @@
     StatusBarHeadsUpChangeListener(
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarWindowController statusBarWindowController,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             KeyguardBypassController keyguardBypassController,
             HeadsUpManagerPhone headsUpManager,
             StatusBarStateController statusBarStateController,
@@ -52,7 +52,7 @@
 
         mNotificationShadeWindowController = notificationShadeWindowController;
         mStatusBarWindowController = statusBarWindowController;
-        mNotificationPanelViewController = notificationPanelViewController;
+        mShadeViewController = shadeViewController;
         mKeyguardBypassController = keyguardBypassController;
         mHeadsUpManager = headsUpManager;
         mStatusBarStateController = statusBarStateController;
@@ -64,14 +64,14 @@
         if (inPinnedMode) {
             mNotificationShadeWindowController.setHeadsUpShowing(true);
             mStatusBarWindowController.setForceStatusBarVisible(true);
-            if (mNotificationPanelViewController.isFullyCollapsed()) {
-                mNotificationPanelViewController.updateTouchableRegion();
+            if (mShadeViewController.isFullyCollapsed()) {
+                mShadeViewController.updateTouchableRegion();
             }
         } else {
             boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
                     && mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
-            if (!mNotificationPanelViewController.isFullyCollapsed()
-                    || mNotificationPanelViewController.isTracking()
+            if (!mShadeViewController.isFullyCollapsed()
+                    || mShadeViewController.isTracking()
                     || bypassKeyguard) {
                 // We are currently tracking or is open and the shade doesn't need to
                 //be kept
@@ -85,7 +85,7 @@
                 //animation
                 // is finished.
                 mHeadsUpManager.setHeadsUpGoingAway(true);
-                mNotificationPanelViewController.getNotificationStackScrollLayoutController()
+                mShadeViewController.getNotificationStackScrollLayoutController()
                         .runAfterAnimationFinished(() -> {
                     if (!mHeadsUpManager.hasPinnedHeadsUp()) {
                         mNotificationShadeWindowController.setHeadsUpShowing(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 70aab61..f7646d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -69,11 +69,11 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.TaskbarDelegate;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionListener;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -251,7 +251,7 @@
     protected LockPatternUtils mLockPatternUtils;
     protected ViewMediatorCallback mViewMediatorCallback;
     @Nullable protected CentralSurfaces mCentralSurfaces;
-    private NotificationPanelViewController mNotificationPanelViewController;
+    private ShadeViewController mShadeViewController;
     private BiometricUnlockController mBiometricUnlockController;
     private boolean mCentralSurfacesRegistered;
 
@@ -371,7 +371,7 @@
 
     @Override
     public void registerCentralSurfaces(CentralSurfaces centralSurfaces,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             ShadeExpansionStateManager shadeExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer,
@@ -380,7 +380,7 @@
         mBiometricUnlockController = biometricUnlockController;
 
         mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
-        mNotificationPanelViewController = notificationPanelViewController;
+        mShadeViewController = shadeViewController;
         if (shadeExpansionStateManager != null) {
             shadeExpansionStateManager.addExpansionListener(this);
         }
@@ -482,8 +482,8 @@
         // Avoid having the shade and the bouncer open at the same time over a dream.
         final boolean hideBouncerOverDream =
                 mDreamOverlayStateController.isOverlayActive()
-                        && (mNotificationPanelViewController.isExpanded()
-                        || mNotificationPanelViewController.isExpanding());
+                        && (mShadeViewController.isExpanded()
+                        || mShadeViewController.isExpanding());
 
         final boolean isUserTrackingStarted =
                 event.getFraction() != EXPANSION_HIDDEN && event.getTracking();
@@ -495,7 +495,7 @@
                 && !mKeyguardStateController.isOccluded()
                 && !mKeyguardStateController.canDismissLockScreen()
                 && !bouncerIsAnimatingAway()
-                && !mNotificationPanelViewController.isUnlockHintRunning()
+                && !mShadeViewController.isUnlockHintRunning()
                 && !(mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED);
     }
 
@@ -699,7 +699,7 @@
         if (mKeyguardStateController.isShowing()) {
             final boolean isOccluded = mKeyguardStateController.isOccluded();
             // Hide quick settings.
-            mNotificationPanelViewController.resetViews(/* animate= */ !isOccluded);
+            mShadeViewController.resetViews(/* animate= */ !isOccluded);
             // Hide bouncer and quick-quick settings.
             if (isOccluded && !mDozing) {
                 mCentralSurfaces.hideKeyguard();
@@ -862,7 +862,7 @@
     public void startPreHideAnimation(Runnable finishRunnable) {
         if (primaryBouncerIsShowing()) {
             mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
-            mNotificationPanelViewController.startBouncerPreHideAnimation();
+            mShadeViewController.startBouncerPreHideAnimation();
 
             // We update the state (which will show the keyguard) only if an animation will run on
             // the keyguard. If there is no animation, we wait before updating the state so that we
@@ -873,12 +873,12 @@
         } else if (finishRunnable != null) {
             finishRunnable.run();
         }
-        mNotificationPanelViewController.blockExpansionForCurrentTouch();
+        mShadeViewController.blockExpansionForCurrentTouch();
     }
 
     @Override
     public void blockPanelExpansionFromCurrentTouch() {
-        mNotificationPanelViewController.blockExpansionForCurrentTouch();
+        mShadeViewController.blockExpansionForCurrentTouch();
     }
 
     @Override
@@ -975,7 +975,7 @@
     public void onKeyguardFadedAway() {
         mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
                         .setKeyguardFadingAway(false), 100);
-        mNotificationPanelViewController.resetViewGroupFade();
+        mShadeViewController.resetViewGroupFade();
         mCentralSurfaces.finishKeyguardFadingAway();
         mBiometricUnlockController.finishKeyguardFadingAway();
         WindowManagerGlobal.getInstance().trimMemory(
@@ -1050,7 +1050,7 @@
             if (hideImmediately) {
                 mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
             } else {
-                mNotificationPanelViewController.expandToNotifications();
+                mShadeViewController.expandToNotifications();
             }
         }
         return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index b1642d6..9f69db9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -21,7 +21,7 @@
     override fun onIntentStarted(willAnimate: Boolean) {
         delegate.onIntentStarted(willAnimate)
         if (willAnimate) {
-            centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(true)
+            centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(true)
         } else {
             centralSurfaces.collapsePanelOnMainThread()
         }
@@ -29,16 +29,16 @@
 
     override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
         delegate.onLaunchAnimationStart(isExpandingFullyAbove)
-        centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(true)
+        centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(true)
         if (!isExpandingFullyAbove) {
-            centralSurfaces.notificationPanelViewController.collapseWithDuration(
+            centralSurfaces.shadeViewController.collapseWithDuration(
                 ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
         }
     }
 
     override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
         delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
-        centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(false)
+        centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(false)
         centralSurfaces.onLaunchAnimationEnd(isExpandingFullyAbove)
     }
 
@@ -48,12 +48,12 @@
         linearProgress: Float
     ) {
         delegate.onLaunchAnimationProgress(state, progress, linearProgress)
-        centralSurfaces.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
+        centralSurfaces.shadeViewController.applyLaunchAnimationProgress(linearProgress)
     }
 
     override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
         delegate.onLaunchAnimationCancelled()
-        centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(false)
+        centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(false)
         centralSurfaces.onLaunchAnimationCancelled(isLaunchForActivity)
     }
 }
\ No newline at end of file
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 c623201..bd5815aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -55,11 +55,10 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -122,7 +121,7 @@
 
     private final CentralSurfaces mCentralSurfaces;
     private final NotificationPresenter mPresenter;
-    private final NotificationPanelViewController mNotificationPanel;
+    private final ShadeViewController mNotificationPanel;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
     private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     private final UserTracker mUserTracker;
@@ -157,7 +156,7 @@
             OnUserInteractionCallback onUserInteractionCallback,
             CentralSurfaces centralSurfaces,
             NotificationPresenter presenter,
-            NotificationPanelViewController panel,
+            ShadeViewController panel,
             ActivityLaunchAnimator activityLaunchAnimator,
             NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
             LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 39362cf..dfaee4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -31,15 +31,14 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.QuickSettingsController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -58,14 +57,12 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -84,9 +81,8 @@
     private final NotifShadeEventSource mNotifShadeEventSource;
     private final NotificationMediaManager mMediaManager;
     private final NotificationGutsManager mGutsManager;
-    private final LockscreenGestureLogger mLockscreenGestureLogger;
 
-    private final NotificationPanelViewController mNotificationPanel;
+    private final ShadeViewController mNotificationPanel;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final AboveShelfObserver mAboveShelfObserver;
     private final DozeScrimController mDozeScrimController;
@@ -109,7 +105,7 @@
     @Inject
     StatusBarNotificationPresenter(
             Context context,
-            NotificationPanelViewController panel,
+            ShadeViewController panel,
             QuickSettingsController quickSettingsController,
             HeadsUpManagerPhone headsUp,
             NotificationShadeWindowView statusBarWindow,
@@ -151,7 +147,6 @@
         mNotifShadeEventSource = notifShadeEventSource;
         mMediaManager = notificationMediaManager;
         mGutsManager = notificationGutsManager;
-        mLockscreenGestureLogger = lockscreenGestureLogger;
         mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
         mNotificationShadeWindowController = notificationShadeWindowController;
         mNotifPipelineFlags = notifPipelineFlags;
@@ -239,34 +234,6 @@
     }
 
     @Override
-    public void onActivated(ActivatableNotificationView view) {
-        onActivated();
-        if (view != null) {
-            mNotificationPanel.getShadeNotificationPresenter().setActivatedChild(view);
-        }
-    }
-
-    public void onActivated() {
-        mLockscreenGestureLogger.write(
-                MetricsEvent.ACTION_LS_NOTE,
-                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
-        mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_NOTIFICATION_FALSE_TOUCH);
-        ActivatableNotificationView previousView =
-                mNotificationPanel.getShadeNotificationPresenter().getActivatedChild();
-        if (previousView != null) {
-            previousView.makeInactive(true /* animate */);
-        }
-    }
-
-    @Override
-    public void onActivationReset(ActivatableNotificationView view) {
-        if (view == mNotificationPanel.getShadeNotificationPresenter().getActivatedChild()) {
-            mNotificationPanel.getShadeNotificationPresenter().setActivatedChild(null);
-            mKeyguardIndicationController.hideTransientIndication();
-        }
-    }
-
-    @Override
     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
         mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation);
     }
@@ -276,7 +243,7 @@
             boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         mCentralSurfaces.wakeUpIfDozing(
-                SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK",
+                SystemClock.uptimeMillis(), "NOTIFICATION_CLICK",
                 PowerManager.WAKE_REASON_GESTURE);
         if (nowExpanded) {
             if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
@@ -364,26 +331,6 @@
                 return true;
             }
 
-            if (sbn.getNotification().fullScreenIntent != null
-                    && !mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()) {
-                // we don't allow head-up on the lockscreen (unless there's a
-                // "showWhenLocked" activity currently showing)  if
-                // the potential HUN has a fullscreen intent
-                if (mKeyguardStateController.isShowing() && !mCentralSurfaces.isOccluded()) {
-                    if (DEBUG) {
-                        Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen "
-                                + sbn.getKey());
-                    }
-                    return true;
-                }
-
-                if (mAccessibilityManager.isTouchExplorationEnabled()) {
-                    if (DEBUG) {
-                        Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
-                    }
-                    return true;
-                }
-            }
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0cd3401..8fa803e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -277,7 +277,7 @@
 
                     // Show AOD. That'll cause the KeyguardVisibilityHelper to call
                     // #animateInKeyguard.
-                    mCentralSurfaces.notificationPanelViewController.showAodUi()
+                    mCentralSurfaces.shadeViewController.showAodUi()
                 }
             }, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong())
 
@@ -326,7 +326,7 @@
         // already expanded and showing notifications/QS, the animation looks really messy. For now,
         // disable it if the notification panel is expanded.
         if ((!this::mCentralSurfaces.isInitialized ||
-                mCentralSurfaces.notificationPanelViewController.isPanelExpanded) &&
+                mCentralSurfaces.shadeViewController.isPanelExpanded) &&
                 // Status bar might be expanded because we have started
                 // playing the animation already
                 !isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 5d4adda..015ee7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -44,6 +44,7 @@
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationsQuickSettingsContainer;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -52,6 +53,7 @@
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
 import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
@@ -76,17 +78,18 @@
 import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.settings.SecureSettings;
 
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoSet;
 
-@Module(subcomponents = StatusBarFragmentComponent.class)
+import java.util.concurrent.Executor;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+
+@Module(subcomponents = StatusBarFragmentComponent.class,
+        includes = { ActivatableNotificationViewModelModule.class })
 public abstract class StatusBarViewModule {
 
     public static final String SHADE_HEADER = "large_screen_shade_header";
@@ -163,6 +166,12 @@
     }
 
     /** */
+    @Binds
+    @CentralSurfacesComponent.CentralSurfacesScope
+    abstract ShadeViewController bindsShadeViewController(
+            NotificationPanelViewController notificationPanelViewController);
+
+    /** */
     @Provides
     @CentralSurfacesComponent.CentralSurfacesScope
     public static LockIconView getLockIconView(
@@ -301,7 +310,7 @@
             StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             KeyguardStateController keyguardStateController,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             StatusBarStateController statusBarStateController,
             CommandQueue commandQueue,
             CarrierConfigTracker carrierConfigTracker,
@@ -324,7 +333,7 @@
                 darkIconManagerFactory,
                 statusBarHideIconsForBouncerManager,
                 keyguardStateController,
-                notificationPanelViewController,
+                shadeViewController,
                 statusBarStateController,
                 commandQueue,
                 carrierConfigTracker,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index fe63994..453dd1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -52,8 +52,8 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.OperatorNameView;
 import com.android.systemui.statusbar.OperatorNameViewController;
@@ -107,7 +107,7 @@
     private PhoneStatusBarView mStatusBar;
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardStateController mKeyguardStateController;
-    private final NotificationPanelViewController mNotificationPanelViewController;
+    private final ShadeViewController mShadeViewController;
     private LinearLayout mEndSideContent;
     private View mClockView;
     private View mOngoingCallChip;
@@ -198,7 +198,7 @@
             StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             KeyguardStateController keyguardStateController,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             StatusBarStateController statusBarStateController,
             CommandQueue commandQueue,
             CarrierConfigTracker carrierConfigTracker,
@@ -221,7 +221,7 @@
         mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
         mDarkIconManagerFactory = darkIconManagerFactory;
         mKeyguardStateController = keyguardStateController;
-        mNotificationPanelViewController = notificationPanelViewController;
+        mShadeViewController = shadeViewController;
         mStatusBarStateController = statusBarStateController;
         mCommandQueue = commandQueue;
         mCarrierConfigTracker = carrierConfigTracker;
@@ -509,7 +509,7 @@
 
     private boolean shouldHideNotificationIcons() {
         if (!mShadeExpansionStateManager.isClosed()
-                && mNotificationPanelViewController.shouldHideStatusBarIconsWhenExpanded()) {
+                && mShadeViewController.shouldHideStatusBarIconsWhenExpanded()) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index a4cb99b..6186c43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -28,8 +28,8 @@
 import com.android.systemui.R;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
 import com.android.systemui.settings.brightness.ToggleSlider;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 
 import java.util.Objects;
@@ -43,7 +43,7 @@
 
     private final NotificationShadeWindowView mStatusBarWindow;
     private final Consumer<Boolean> mVisibilityCallback;
-    private final NotificationPanelViewController mNotificationPanel;
+    private final ShadeViewController mNotificationPanel;
     private final NotificationShadeDepthController mDepthController;
     private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>();
     private final BrightnessSliderController.Factory mToggleSliderFactory;
@@ -54,7 +54,7 @@
     private int mLastBrightnessSliderWidth = -1;
 
     public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             NotificationShadeDepthController notificationShadeDepthController,
             BrightnessSliderController.Factory factory,
             @NonNull Consumer<Boolean> visibilityCallback) {
@@ -62,7 +62,7 @@
         mToggleSliderFactory = factory;
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror_container);
         mToggleSliderController = setMirrorLayout();
-        mNotificationPanel = notificationPanelViewController;
+        mNotificationPanel = shadeViewController;
         mDepthController = notificationShadeDepthController;
         mNotificationPanel.setAlphaChangeAnimationEndAction(() -> {
             mBrightnessMirror.setVisibility(View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a08aa88..403a7e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -21,7 +21,6 @@
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
 
 import android.app.ActivityManager;
-import android.app.Notification;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
@@ -135,7 +134,6 @@
     @Nullable
     private RevealParams mRevealParams;
     private Rect mContentBackgroundBounds;
-    private boolean mIsFocusAnimationFlagActive;
     private boolean mIsAnimatingAppearance = false;
 
     // TODO(b/193539698): move these to a Controller
@@ -433,7 +431,7 @@
         // case to prevent flicker.
         if (!mRemoved) {
             ViewGroup parent = (ViewGroup) getParent();
-            if (animate && parent != null && mIsFocusAnimationFlagActive) {
+            if (animate && parent != null) {
 
                 ViewGroup grandParent = (ViewGroup) parent.getParent();
                 ViewGroupOverlay overlay = parent.getOverlay();
@@ -598,24 +596,10 @@
     }
 
     /**
-     * Sets whether the feature flag for the revised inline reply animation is active or not.
-     * @param active
-     */
-    public void setIsFocusAnimationFlagActive(boolean active) {
-        mIsFocusAnimationFlagActive = active;
-    }
-
-    /**
      * Focuses the RemoteInputView and animates its appearance
      */
     public void focusAnimated() {
-        if (!mIsFocusAnimationFlagActive && getVisibility() != VISIBLE
-                && mRevealParams != null) {
-            android.animation.Animator animator = mRevealParams.createCircularRevealAnimator(this);
-            animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            animator.start();
-        } else if (mIsFocusAnimationFlagActive && getVisibility() != VISIBLE) {
+        if (getVisibility() != VISIBLE) {
             mIsAnimatingAppearance = true;
             setAlpha(0f);
             Animator focusAnimator = getFocusAnimator(getActionsContainerLayout());
@@ -670,37 +654,19 @@
     }
 
     private void reset() {
-        if (mIsFocusAnimationFlagActive) {
-            mProgressBar.setVisibility(INVISIBLE);
-            mResetting = true;
-            mSending = false;
-            onDefocus(true /* animate */, false /* logClose */, () -> {
-                mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
-                mEditText.getText().clear();
-                mEditText.setEnabled(isAggregatedVisible());
-                mSendButton.setVisibility(VISIBLE);
-                mController.removeSpinning(mEntry.getKey(), mToken);
-                updateSendButton();
-                setAttachment(null);
-                mResetting = false;
-            });
-            return;
-        }
-
+        mProgressBar.setVisibility(INVISIBLE);
         mResetting = true;
         mSending = false;
-        mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
-
-        mEditText.getText().clear();
-        mEditText.setEnabled(isAggregatedVisible());
-        mSendButton.setVisibility(VISIBLE);
-        mProgressBar.setVisibility(INVISIBLE);
-        mController.removeSpinning(mEntry.getKey(), mToken);
-        updateSendButton();
-        onDefocus(false /* animate */, false /* logClose */, null /* doAfterDefocus */);
-        setAttachment(null);
-
-        mResetting = false;
+        onDefocus(true /* animate */, false /* logClose */, () -> {
+            mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
+            mEditText.getText().clear();
+            mEditText.setEnabled(isAggregatedVisible());
+            mSendButton.setVisibility(VISIBLE);
+            mController.removeSpinning(mEntry.getKey(), mToken);
+            updateSendButton();
+            setAttachment(null);
+            mResetting = false;
+        });
     }
 
     @Override
@@ -844,7 +810,7 @@
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
-        if (mIsFocusAnimationFlagActive) setPivotY(getMeasuredHeight());
+        setPivotY(getMeasuredHeight());
         if (mContentBackgroundBounds != null) {
             mContentBackground.setBounds(mContentBackgroundBounds);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index 22b4c9d..e9b1d54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -30,8 +30,6 @@
 import android.view.View
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.RemoteInputController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -63,8 +61,6 @@
 
     var revealParams: RevealParams?
 
-    val isFocusAnimationFlagActive: Boolean
-
     /**
      * Sets the smart reply that should be inserted in the remote input, or `null` if the user is
      * not editing a smart reply.
@@ -122,7 +118,6 @@
     private val remoteInputController: RemoteInputController,
     private val shortcutManager: ShortcutManager,
     private val uiEventLogger: UiEventLogger,
-    private val mFlags: FeatureFlags
 ) : RemoteInputViewController {
 
     private val onSendListeners = ArraySet<OnSendRemoteInputListener>()
@@ -154,9 +149,6 @@
 
     override val isActive: Boolean get() = view.isActive
 
-    override val isFocusAnimationFlagActive: Boolean
-        get() = mFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION)
-
     override fun bind() {
         if (isBound) return
         isBound = true
@@ -167,7 +159,6 @@
             view.setSupportedMimeTypes(it.allowedDataTypes)
         }
         view.setRevealParameters(revealParams)
-        view.setIsFocusAnimationFlagActive(isFocusAnimationFlagActive)
 
         view.addOnEditTextFocusChangedListener(onFocusChangeListener)
         view.addOnSendRemoteInputListener(onSendRemoteInputListener)
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 3805019..412b315 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -353,6 +353,8 @@
                     // before CoreStartables run, and will not be removed.
                     // In many cases, it reports the battery level of the stylus.
                     registerBatteryListener(deviceId)
+                } else if (device.bluetoothAddress != null) {
+                    onStylusBluetoothConnected(deviceId, device.bluetoothAddress)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index d1bd73a..d74906a 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -39,11 +39,11 @@
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.settings.GlobalSettings
 import dagger.Lazy
-import java.util.function.Consumer
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
+import java.util.function.Consumer
+import javax.inject.Inject
 
 /**
  * Controls folding to AOD animation: when AOD is enabled and foldable device is folded we play a
@@ -128,7 +128,7 @@
     }
 
     private fun getShadeFoldAnimator(): ShadeFoldAnimator =
-        centralSurfaces.notificationPanelViewController.shadeFoldAnimator
+        centralSurfaces.shadeViewController.shadeFoldAnimator
 
     private fun setAnimationState(playing: Boolean) {
         shouldPlayAnimation = playing
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index aa26e68..77210b7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -2344,7 +2344,7 @@
     }
 
     @VisibleForTesting
-    void clearInternalHandleAfterTest() {
+    void clearInternalHandlerAfterTest() {
         if (mHandler != null) {
             mHandler.removeCallbacksAndMessages(null);
         }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index a9920ec7..8f4b320 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -101,7 +101,8 @@
         whenever(smallClockController.events).thenReturn(smallClockEvents)
         whenever(largeClockController.events).thenReturn(largeClockEvents)
         whenever(clock.events).thenReturn(events)
-        whenever(clock.animations).thenReturn(animations)
+        whenever(smallClockController.animations).thenReturn(animations)
+        whenever(largeClockController.animations).thenReturn(animations)
         whenever(smallClockController.config)
             .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
         whenever(largeClockController.config)
@@ -184,7 +185,7 @@
         keyguardCaptor.value.onKeyguardVisibilityChanged(true)
         batteryCaptor.value.onBatteryLevelChanged(10, false, true)
 
-        verify(animations).charge()
+        verify(animations, times(2)).charge()
     }
 
     @Test
@@ -198,7 +199,7 @@
             batteryCaptor.value.onBatteryLevelChanged(10, false, true)
             batteryCaptor.value.onBatteryLevelChanged(10, false, true)
 
-            verify(animations, times(1)).charge()
+            verify(animations, times(2)).charge()
         }
 
     @Test
@@ -246,7 +247,7 @@
         verify(animations, never()).doze(0f)
 
         captor.value.onKeyguardVisibilityChanged(false)
-        verify(animations, times(1)).doze(0f)
+        verify(animations, times(2)).doze(0f)
     }
 
     @Test
@@ -284,7 +285,7 @@
 
         yield()
 
-        verify(animations).doze(0.4f)
+        verify(animations, times(2)).doze(0.4f)
 
         job.cancel()
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index fc906de..95db0c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -184,13 +184,14 @@
         when(mClockController.getEvents()).thenReturn(mClockEvents);
         when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents);
         when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents);
-        when(mClockController.getAnimations()).thenReturn(mClockAnimations);
+        when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations);
+        when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations);
         when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
         when(mClockEventController.getClock()).thenReturn(mClockController);
         when(mSmallClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
         when(mLargeClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
 
         mSliceView = new View(getContext());
         when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
@@ -384,9 +385,9 @@
         assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
 
         when(mSmallClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
         when(mLargeClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
         verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
         listenerArgumentCaptor.getValue().onCurrentClockChanged();
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 2c1d2ad..a2c6329 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -130,7 +130,7 @@
     public void updatePosition_primaryClockAnimation() {
         ClockController mockClock = mock(ClockController.class);
         when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
-        when(mockClock.getConfig()).thenReturn(new ClockConfig(false, false, true));
+        when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true));
 
         mController.updatePosition(10, 15, 20f, true);
 
@@ -145,7 +145,7 @@
     public void updatePosition_alternateClockAnimation() {
         ClockController mockClock = mock(ClockController.class);
         when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
-        when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true, true));
+        when(mockClock.getConfig()).thenReturn(new ClockConfig(true, true));
 
         mController.updatePosition(10, 15, 20f, true);
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 3eb9590..ed40eed 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -155,6 +155,7 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
 import org.mockito.internal.util.reflection.FieldSetter;
+import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -303,6 +304,7 @@
 
         mMockitoSession = ExtendedMockito.mockitoSession()
                 .spyStatic(SubscriptionManager.class)
+                .strictness(Strictness.WARN)
                 .startMocking();
         ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                 .when(SubscriptionManager::getDefaultSubscriptionId);
@@ -2528,19 +2530,6 @@
     }
 
     @Test
-    public void testBatteryChangedIntent_unplugDevice_resetIncompatibleCharger() {
-        mKeyguardUpdateMonitor.mIncompatibleCharger = true;
-        Intent batteryChangedIntent =
-                getBatteryIntent().putExtra(BatteryManager.EXTRA_PLUGGED, -1);
-
-        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(mContext, batteryChangedIntent);
-
-        BatteryStatus status = verifyRefreshBatteryInfo();
-        assertThat(status.incompatibleCharger.get()).isFalse();
-        assertThat(mKeyguardUpdateMonitor.mIncompatibleCharger).isFalse();
-    }
-
-    @Test
     public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard()
             throws RemoteException {
         // GIVEN shouldTriggerActiveUnlock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
new file mode 100644
index 0000000..aff52f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.accessibility.data.repository
+
+import android.testing.AndroidTestingRunner
+import android.view.accessibility.AccessibilityManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AccessibilityRepositoryTest : SysuiTestCase() {
+
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    // mocks
+    @Mock private lateinit var a11yManager: AccessibilityManager
+
+    // real impls
+    private val underTest by lazy { AccessibilityRepository(a11yManager) }
+
+    @Test
+    fun isTouchExplorationEnabled_reflectsA11yManager_initFalse() = runTest {
+        whenever(a11yManager.isTouchExplorationEnabled).thenReturn(false)
+        val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
+        assertThat(isTouchExplorationEnabled).isFalse()
+    }
+
+    @Test
+    fun isTouchExplorationEnabled_reflectsA11yManager_initTrue() = runTest {
+        whenever(a11yManager.isTouchExplorationEnabled).thenReturn(true)
+        val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
+        assertThat(isTouchExplorationEnabled).isTrue()
+    }
+
+    @Test
+    fun isTouchExplorationEnabled_reflectsA11yManager_changeTrue() = runTest {
+        whenever(a11yManager.isTouchExplorationEnabled).thenReturn(false)
+        val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
+        runCurrent()
+        withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) }
+            .onTouchExplorationStateChanged(/* enabled = */ true)
+        assertThat(isTouchExplorationEnabled).isTrue()
+    }
+
+    @Test
+    fun isTouchExplorationEnabled_reflectsA11yManager_changeFalse() = runTest {
+        whenever(a11yManager.isTouchExplorationEnabled).thenReturn(true)
+        val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled)
+        runCurrent()
+        withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) }
+            .onTouchExplorationStateChanged(/* enabled = */ false)
+        assertThat(isTouchExplorationEnabled).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
index dd87f6e..f4dacab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
@@ -18,6 +18,8 @@
 
 import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
 import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
+import android.hardware.biometrics.BiometricConstants
+import android.hardware.face.FaceManager
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import android.view.View
@@ -34,6 +36,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.times
 import org.mockito.junit.MockitoJUnit
 
 
@@ -98,7 +101,7 @@
     }
 
     @Test
-    fun ignoresFaceErrors() {
+    fun ignoresFaceErrors_faceIsNotClass3_notLockoutError() {
         biometricView.onDialogAnimatedIn()
         biometricView.onError(TYPE_FACE, "not a face")
         waitForIdleSync()
@@ -113,5 +116,47 @@
         verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR)
     }
 
+    @Test
+    fun doNotIgnoresFaceErrors_faceIsClass3_notLockoutError() {
+        biometricView.isFaceClass3 = true
+        biometricView.onDialogAnimatedIn()
+        biometricView.onError(TYPE_FACE, "not a face")
+        waitForIdleSync()
+
+        assertThat(biometricView.isAuthenticating).isTrue()
+        verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR)
+
+        biometricView.onError(TYPE_FINGERPRINT, "that's a nope")
+        TestableLooper.get(this).moveTimeForward(1000)
+        waitForIdleSync()
+
+        verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR)
+    }
+
+    @Test
+    fun doNotIgnoresFaceErrors_faceIsClass3_lockoutError() {
+        biometricView.isFaceClass3 = true
+        biometricView.onDialogAnimatedIn()
+        biometricView.onError(
+            TYPE_FACE,
+            FaceManager.getErrorString(
+                biometricView.context,
+                BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+                0 /*vendorCode */
+            )
+        )
+        waitForIdleSync()
+
+        assertThat(biometricView.isAuthenticating).isTrue()
+        verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR)
+
+        biometricView.onError(TYPE_FINGERPRINT, "that's a nope")
+        TestableLooper.get(this).moveTimeForward(1000)
+        waitForIdleSync()
+
+        verify(callback, times(2)).onAction(AuthBiometricView.Callback.ACTION_ERROR)
+    }
+
+
     override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 80c3e5e..937a7a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -172,64 +172,64 @@
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - status bar state is keyguard - returns true`() {
+    fun canCameraGestureBeLaunched_statusBarStateIsKeyguard_returnsTrue() {
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - state is shade-locked - returns true`() {
+    fun canCameraGestureBeLaunched_stateIsShadeLocked_returnsTrue() {
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - state is keyguard - camera activity on top - returns true`() {
+    fun canCameraGestureBeLaunched_stateIsKeyguard_cameraActivityOnTop_returnsTrue() {
         prepare(isCameraActivityRunningOnTop = true)
 
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - state is shade-locked - camera activity on top - true`() {
+    fun canCameraGestureBeLaunched_stateIsShadeLocked_cameraActivityOnTop_true() {
         prepare(isCameraActivityRunningOnTop = true)
 
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - not allowed by admin - returns false`() {
+    fun canCameraGestureBeLaunched_notAllowedByAdmin_returnsFalse() {
         prepare(isCameraAllowedByAdmin = false)
 
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - intent does not resolve to any app - returns false`() {
+    fun canCameraGestureBeLaunched_intentDoesNotResolveToAnyApp_returnsFalse() {
         prepare(installedCameraAppCount = 0)
 
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - state is shade - no running tasks - returns true`() {
+    fun canCameraGestureBeLaunched_stateIsShade_noRunningTasks_returnsTrue() {
         prepare(isCameraActivityRunningOnTop = false, isTaskListEmpty = true)
 
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - state is shade - camera activity on top - returns false`() {
+    fun canCameraGestureBeLaunched_stateIsShade_cameraActivityOnTop_returnsFalse() {
         prepare(isCameraActivityRunningOnTop = true)
 
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isFalse()
     }
 
     @Test
-    fun `canCameraGestureBeLaunched - state is shade - camera activity not on top - true`() {
+    fun canCameraGestureBeLaunched_stateIsShade_cameraActivityNotOnTop_true() {
         assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
     }
 
     @Test
-    fun `launchCamera - only one camera app installed - using secure screen lock option`() {
+    fun launchCamera_onlyOneCameraAppInstalled_usingSecureScreenLockOption() {
         val source = 1337
 
         underTest.launchCamera(source)
@@ -238,7 +238,7 @@
     }
 
     @Test
-    fun `launchCamera - only one camera app installed - using non-secure screen lock option`() {
+    fun launchCamera_onlyOneCameraAppInstalled_usingNonSecureScreenLockOption() {
         prepare(isUsingSecureScreenLockOption = false)
         val source = 1337
 
@@ -248,7 +248,7 @@
     }
 
     @Test
-    fun `launchCamera - multiple camera apps installed - using secure screen lock option`() {
+    fun launchCamera_multipleCameraAppsInstalled_usingSecureScreenLockOption() {
         prepare(installedCameraAppCount = 2)
         val source = 1337
 
@@ -262,7 +262,7 @@
     }
 
     @Test
-    fun `launchCamera - multiple camera apps installed - using non-secure screen lock option`() {
+    fun launchCamera_multipleCameraAppsInstalled_usingNonSecureScreenLockOption() {
         prepare(
             isUsingSecureScreenLockOption = false,
             installedCameraAppCount = 2,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 8600b7c..fe5fa1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -25,7 +25,6 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
-import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -121,7 +120,6 @@
         mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
                 new ClipData.Item("Test Item"));
 
-        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, false);
 
         mOverlayController = new ClipboardOverlayController(
                 mContext,
@@ -234,7 +232,6 @@
 
     @Test
     public void test_remoteCopy_withFlagOn() {
-        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
         when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
 
         mOverlayController.setClipData(mSampleClipData, "");
@@ -243,17 +240,7 @@
     }
 
     @Test
-    public void test_remoteCopy_withFlagOff() {
-        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
-
-        mOverlayController.setClipData(mSampleClipData, "");
-
-        verify(mTimeoutHandler).resetTimeout();
-    }
-
-    @Test
     public void test_nonRemoteCopy() {
-        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
         when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false);
 
         mOverlayController.setClipData(mSampleClipData, "");
@@ -279,7 +266,6 @@
     public void test_logOnClipboardActionsShown() {
         ClipData.Item item = mSampleClipData.getItemAt(0);
         item.setTextLinks(Mockito.mock(TextLinks.class));
-        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
         when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString()))
                 .thenReturn(true);
         when(mClipboardUtils.getAction(any(TextLinks.class), anyString()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index fe352fd..1b2fc93d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -72,7 +72,7 @@
     }
 
     @Test
-    fun `long-press`() = runTest {
+    fun longPress() = runTest {
         val downX = 123
         val downY = 456
         dispatchTouchEvents(
@@ -91,7 +91,7 @@
     }
 
     @Test
-    fun `long-press but feature not enabled`() = runTest {
+    fun longPressButFeatureNotEnabled() = runTest {
         underTest.isLongPressHandlingEnabled = false
         dispatchTouchEvents(
             Down(
@@ -106,7 +106,7 @@
     }
 
     @Test
-    fun `long-press but view not attached`() = runTest {
+    fun longPressButViewNotAttached() = runTest {
         isAttachedToWindow = false
         dispatchTouchEvents(
             Down(
@@ -121,7 +121,7 @@
     }
 
     @Test
-    fun `dragged too far to be considered a long-press`() = runTest {
+    fun draggedTooFarToBeConsideredAlongPress() = runTest {
         dispatchTouchEvents(
             Down(
                 x = 123,
@@ -138,7 +138,7 @@
     }
 
     @Test
-    fun `held down too briefly to be considered a long-press`() = runTest {
+    fun heldDownTooBrieflyToBeConsideredAlongPress() = runTest {
         dispatchTouchEvents(
             Down(
                 x = 123,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 87c66b5..75eec72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -74,7 +74,7 @@
     }
 
     @Test
-    fun `demo command flow - returns args`() =
+    fun demoCommandFlow_returnsArgs() =
         testScope.runTest {
             var latest: Bundle? = null
             val flow = underTest.demoFlowForCommand(TEST_COMMAND)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
index 5704ef3..872c079 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
@@ -28,7 +28,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -49,7 +49,7 @@
     CentralSurfaces mCentralSurfaces;
 
     @Mock
-    NotificationPanelViewController mNotificationPanelViewController;
+    ShadeViewController mShadeViewController;
 
     @Mock
     DreamTouchHandler.TouchSession mTouchSession;
@@ -63,8 +63,8 @@
         MockitoAnnotations.initMocks(this);
         mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces),
                 TOUCH_HEIGHT);
-        when(mCentralSurfaces.getNotificationPanelViewController())
-                .thenReturn(mNotificationPanelViewController);
+        when(mCentralSurfaces.getShadeViewController())
+                .thenReturn(mShadeViewController);
     }
 
     /**
@@ -97,7 +97,7 @@
     }
 
     /**
-     * Ensure touches are propagated to the {@link NotificationPanelViewController}.
+     * Ensure touches are propagated to the {@link ShadeViewController}.
      */
     @Test
     public void testEventPropagation() {
@@ -110,7 +110,7 @@
         mTouchHandler.onSessionStart(mTouchSession);
         verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
         inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
-        verify(mNotificationPanelViewController).handleExternalTouch(motionEvent);
+        verify(mShadeViewController).handleExternalTouch(motionEvent);
     }
 
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
index a2dc1eb..4ba1bc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -5,7 +5,6 @@
 import android.test.suitebuilder.annotation.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.qs.QSFragment
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -13,9 +12,7 @@
 
 @SmallTest
 class FragmentServiceTest : SysuiTestCase() {
-    private val fragmentCreator = TestFragmentCreator()
-    private val fragmenetHostManagerFactory: FragmentHostManager.Factory = mock()
-    private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator }
+    private val fragmentHostManagerFactory: FragmentHostManager.Factory = mock()
 
     private lateinit var fragmentService: FragmentService
 
@@ -25,65 +22,29 @@
             Looper.prepare()
         }
 
-        fragmentService =
-            FragmentService(
-                fragmentCreatorFactory,
-                fragmenetHostManagerFactory,
-                mock(),
-                DumpManager()
-            )
-    }
-
-    @Test
-    fun constructor_addsFragmentCreatorMethodsToMap() {
-        val map = fragmentService.injectionMap
-        assertThat(map).hasSize(2)
-        assertThat(map.keys).contains(QSFragment::class.java.name)
-        assertThat(map.keys).contains(TestFragmentInCreator::class.java.name)
+        fragmentService = FragmentService(fragmentHostManagerFactory, mock(), DumpManager())
     }
 
     @Test
     fun addFragmentInstantiationProvider_objectHasNoFragmentMethods_nothingAdded() {
-        fragmentService.addFragmentInstantiationProvider(Object())
+        fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) {
+            TestFragment()
+        }
 
-        assertThat(fragmentService.injectionMap).hasSize(2)
-    }
-
-    @Test
-    fun addFragmentInstantiationProvider_objectHasFragmentMethods_methodsAdded() {
-        fragmentService.addFragmentInstantiationProvider(
-            @Suppress("unused")
-            object : Any() {
-                fun createTestFragment2() = TestFragment2()
-                fun createTestFragment3() = TestFragment3()
-            }
-        )
-
-        val map = fragmentService.injectionMap
-        assertThat(map).hasSize(4)
-        assertThat(map.keys).contains(TestFragment2::class.java.name)
-        assertThat(map.keys).contains(TestFragment3::class.java.name)
+        assertThat(fragmentService.injectionMap).hasSize(1)
     }
 
     @Test
     fun addFragmentInstantiationProvider_objectFragmentMethodsAlreadyProvided_nothingAdded() {
-        fragmentService.addFragmentInstantiationProvider(
-            @Suppress("unused")
-            object : Any() {
-                fun createTestFragment() = TestFragmentInCreator()
-            }
-        )
+        fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) {
+            TestFragment()
+        }
+        fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) {
+            TestFragment()
+        }
 
-        assertThat(fragmentService.injectionMap).hasSize(2)
+        assertThat(fragmentService.injectionMap).hasSize(1)
     }
 
-    class TestFragmentCreator : FragmentService.FragmentCreator {
-        override fun createQSFragment(): QSFragment = mock()
-        @Suppress("unused")
-        fun createTestFragment(): TestFragmentInCreator = TestFragmentInCreator()
-    }
-
-    class TestFragmentInCreator : Fragment()
-    class TestFragment2 : Fragment()
-    class TestFragment3 : Fragment()
+    class TestFragment : Fragment()
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 1044131..4daecd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -211,7 +211,7 @@
     }
 
     @Test
-    fun `onAttachInfo - reportsContext`() {
+    fun onAttachInfo_reportsContext() {
         val callback: SystemUIAppComponentFactoryBase.ContextAvailableCallback = mock()
         underTest.setContextAvailableCallback(callback)
 
@@ -254,7 +254,7 @@
     }
 
     @Test
-    fun `insert and query selection`() =
+    fun insertAndQuerySelection() =
         testScope.runTest {
             val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
             val affordanceId = AFFORDANCE_2
@@ -278,7 +278,7 @@
         }
 
     @Test
-    fun `query slots`() =
+    fun querySlotsProvidesTwoSlots() =
         testScope.runTest {
             assertThat(querySlots())
                 .isEqualTo(
@@ -296,7 +296,7 @@
         }
 
     @Test
-    fun `query affordances`() =
+    fun queryAffordancesProvidesTwoAffordances() =
         testScope.runTest {
             assertThat(queryAffordances())
                 .isEqualTo(
@@ -316,7 +316,7 @@
         }
 
     @Test
-    fun `delete and query selection`() =
+    fun deleteAndQuerySelection() =
         testScope.runTest {
             insertSelection(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -351,7 +351,7 @@
         }
 
     @Test
-    fun `delete all selections in a slot`() =
+    fun deleteAllSelectionsInAslot() =
         testScope.runTest {
             insertSelection(
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index 5bb8367..e20d3af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -73,7 +73,7 @@
     }
 
     @Test
-    fun `affordance triggered -- camera launch called`() {
+    fun affordanceTriggered_cameraLaunchCalled() {
         // When
         val result = underTest.onTriggered(null)
 
@@ -84,7 +84,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - default when launchable`() =
+    fun getPickerScreenState_defaultWhenLaunchable() =
         testScope.runTest {
             setLaunchable(true)
 
@@ -93,7 +93,7 @@
         }
 
     @Test
-    fun `getPickerScreenState - unavailable when camera app not installed`() =
+    fun getPickerScreenState_unavailableWhenCameraAppNotInstalled() =
         testScope.runTest {
             setLaunchable(isCameraAppInstalled = false)
 
@@ -102,7 +102,7 @@
         }
 
     @Test
-    fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+    fun getPickerScreenState_unavailableWhenCameraDisabledByAdmin() =
         testScope.runTest {
             setLaunchable(isCameraDisabledByDeviceAdmin = true)
 
@@ -111,7 +111,7 @@
         }
 
     @Test
-    fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
+    fun getPickerScreenState_unavailableWhenSecureCameraDisabledByAdmin() =
         testScope.runTest {
             setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 64839e2..c326a86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -97,7 +97,7 @@
     }
 
     @Test
-    fun `dnd not available - picker state hidden`() =
+    fun dndNotAvailable_pickerStateHidden() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(false)
@@ -113,7 +113,7 @@
         }
 
     @Test
-    fun `dnd available - picker state visible`() =
+    fun dndAvailable_pickerStateVisible() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -132,7 +132,7 @@
         }
 
     @Test
-    fun `onTriggered - dnd mode is not ZEN_MODE_OFF - set to ZEN_MODE_OFF`() =
+    fun onTriggered_dndModeIsNotZEN_MODE_OFF_setToZEN_MODE_OFF() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -157,7 +157,7 @@
         }
 
     @Test
-    fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting FOREVER - set zen without condition`() =
+    fun onTriggered_dndModeIsZEN_MODE_OFF_settingFOREVER_setZenWithoutCondition() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -182,7 +182,7 @@
         }
 
     @Test
-    fun `onTriggered - dnd ZEN_MODE_OFF - setting not FOREVER or PROMPT - zen with condition`() =
+    fun onTriggered_dndZEN_MODE_OFF_settingNotFOREVERorPROMPT_zenWithCondition() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -207,7 +207,7 @@
         }
 
     @Test
-    fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is PROMPT - show dialog`() =
+    fun onTriggered_dndModeIsZEN_MODE_OFF_settingIsPROMPT_showDialog() =
         testScope.runTest {
             // given
             val expandable: Expandable = mock()
@@ -230,7 +230,7 @@
         }
 
     @Test
-    fun `lockScreenState - dndAvailable starts as true - change to false - State is Hidden`() =
+    fun lockScreenState_dndAvailableStartsAsTrue_changeToFalse_StateIsHidden() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -249,7 +249,7 @@
         }
 
     @Test
-    fun `lockScreenState - dndMode starts as ZEN_MODE_OFF - change to not OFF - State Visible`() =
+    fun lockScreenState_dndModeStartsAsZEN_MODE_OFF_changeToNotOFF_StateVisible() =
         testScope.runTest {
             // given
             whenever(zenModeController.isZenAvailable).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index 31391ee..292d067 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -60,7 +60,7 @@
     }
 
     @Test
-    fun `flashlight is off -- triggered -- icon is on and active`() = runTest {
+    fun flashlightIsOff_triggered_iconIsOnAndActive() = runTest {
         // given
         flashlightController.isEnabled = false
         flashlightController.isAvailable = true
@@ -83,7 +83,7 @@
     }
 
     @Test
-    fun `flashlight is on -- triggered -- icon is off and inactive`() = runTest {
+    fun flashlightIsOn_triggered_iconIsOffAndInactive() = runTest {
         // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = true
@@ -106,7 +106,7 @@
     }
 
     @Test
-    fun `flashlight is on -- receives error -- icon is off and inactive`() = runTest {
+    fun flashlightIsOn_receivesError_iconIsOffAndInactive() = runTest {
         // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = false
@@ -129,7 +129,7 @@
     }
 
     @Test
-    fun `flashlight availability now off -- hidden`() = runTest {
+    fun flashlightAvailabilityNowOff_hidden() = runTest {
         // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = false
@@ -146,7 +146,7 @@
     }
 
     @Test
-    fun `flashlight availability now on -- flashlight on -- inactive and icon off`() = runTest {
+    fun flashlightAvailabilityNowOn_flashlightOn_inactiveAndIconOff() = runTest {
         // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = false
@@ -168,7 +168,7 @@
     }
 
     @Test
-    fun `flashlight availability now on -- flashlight off -- inactive and icon off`() = runTest {
+    fun flashlightAvailabilityNowOn_flashlightOff_inactiveAndIconOff() = runTest {
         // given
         flashlightController.isEnabled = false
         flashlightController.isAvailable = false
@@ -190,7 +190,7 @@
     }
 
     @Test
-    fun `flashlight available -- picker state default`() = runTest {
+    fun flashlightAvailable_pickerStateDefault() = runTest {
         // given
         flashlightController.isAvailable = true
 
@@ -202,7 +202,7 @@
     }
 
     @Test
-    fun `flashlight not available -- picker state unavailable`() = runTest {
+    fun flashlightNotAvailable_pickerStateUnavailable() = runTest {
         // given
         flashlightController.isAvailable = false
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 2c1c04c..26f0cdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -61,7 +61,7 @@
     }
 
     @Test
-    fun `state - when cannot show while locked - returns Hidden`() = runBlockingTest {
+    fun state_whenCannotShowWhileLocked_returnsHidden() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
         whenever(component.isEnabled()).thenReturn(true)
         whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
@@ -81,7 +81,7 @@
     }
 
     @Test
-    fun `state - when listing controller is missing - returns Hidden`() = runBlockingTest {
+    fun state_whenListingControllerIsMissing_returnsHidden() = runBlockingTest {
         whenever(component.isEnabled()).thenReturn(true)
         whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
         whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
@@ -100,7 +100,7 @@
     }
 
     @Test
-    fun `onQuickAffordanceTriggered - canShowWhileLockedSetting is true`() = runBlockingTest {
+    fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsTrue() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
 
         val onClickedResult = underTest.onTriggered(expandable)
@@ -110,7 +110,7 @@
     }
 
     @Test
-    fun `onQuickAffordanceTriggered - canShowWhileLockedSetting is false`() = runBlockingTest {
+    fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsFalse() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
 
         val onClickedResult = underTest.onTriggered(expandable)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 3bae7f7..9a18ba8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -106,7 +106,7 @@
     }
 
     @Test
-    fun `Setting a setting selects the affordance`() =
+    fun settingAsettingSelectsTheAffordance() =
         testScope.runTest {
             val job = underTest.startSyncing()
 
@@ -129,7 +129,7 @@
         }
 
     @Test
-    fun `Clearing a setting selects the affordance`() =
+    fun clearingAsettingSelectsTheAffordance() =
         testScope.runTest {
             val job = underTest.startSyncing()
 
@@ -156,7 +156,7 @@
         }
 
     @Test
-    fun `Selecting an affordance sets its setting`() =
+    fun selectingAnAffordanceSetsItsSetting() =
         testScope.runTest {
             val job = underTest.startSyncing()
 
@@ -172,7 +172,7 @@
         }
 
     @Test
-    fun `Unselecting an affordance clears its setting`() =
+    fun unselectingAnAffordanceClearsItsSetting() =
         testScope.runTest {
             val job = underTest.startSyncing()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 1259b47..6989f44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -164,7 +164,7 @@
     }
 
     @Test
-    fun `remembers selections by user`() = runTest {
+    fun remembersSelectionsByUser() = runTest {
         overrideResource(
             R.array.config_keyguardQuickAffordanceDefaults,
             arrayOf<String>(),
@@ -246,7 +246,7 @@
     }
 
     @Test
-    fun `selections respects defaults`() = runTest {
+    fun selectionsRespectsDefaults() = runTest {
         val slotId1 = "slot1"
         val slotId2 = "slot2"
         val affordanceId1 = "affordance1"
@@ -277,7 +277,7 @@
     }
 
     @Test
-    fun `selections ignores defaults after selecting an affordance`() = runTest {
+    fun selectionsIgnoresDefaultsAfterSelectingAnAffordance() = runTest {
         val slotId1 = "slot1"
         val slotId2 = "slot2"
         val affordanceId1 = "affordance1"
@@ -309,7 +309,7 @@
     }
 
     @Test
-    fun `selections ignores defaults after clearing a slot`() = runTest {
+    fun selectionsIgnoresDefaultsAfterClearingAslot() = runTest {
         val slotId1 = "slot1"
         val slotId2 = "slot2"
         val affordanceId1 = "affordance1"
@@ -341,7 +341,7 @@
     }
 
     @Test
-    fun `responds to backup and restore by reloading the selections from disk`() = runTest {
+    fun respondsToBackupAndRestoreByReloadingTheSelectionsFromDisk() = runTest {
         overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
         val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
         val job =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index c08ef42..a1c9f87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -112,7 +112,7 @@
     }
 
     @Test
-    fun `selections - primary user process`() =
+    fun selections_primaryUserProcess() =
         testScope.runTest {
             val values = mutableListOf<Map<String, List<String>>>()
             val job = launch { underTest.selections.toList(values) }
@@ -163,7 +163,7 @@
         }
 
     @Test
-    fun `selections - secondary user process - always empty`() =
+    fun selections_secondaryUserProcess_alwaysEmpty() =
         testScope.runTest {
             whenever(userHandle.isSystem).thenReturn(false)
             val values = mutableListOf<Map<String, List<String>>>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index 925c06f..c38827a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -85,7 +85,7 @@
     }
 
     @Test
-    fun `picker state - volume fixed - not available`() = testScope.runTest {
+    fun pickerState_volumeFixed_notAvailable() = testScope.runTest {
         //given
         whenever(audioManager.isVolumeFixed).thenReturn(true)
 
@@ -97,7 +97,7 @@
     }
 
     @Test
-    fun `picker state - volume not fixed - available`() = testScope.runTest {
+    fun pickerState_volumeNotFixed_available() = testScope.runTest {
         //given
         whenever(audioManager.isVolumeFixed).thenReturn(false)
 
@@ -109,7 +109,7 @@
     }
 
     @Test
-    fun `triggered - state was previously NORMAL - currently SILENT - move to previous state`() = testScope.runTest {
+    fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() = testScope.runTest {
         //given
         val ringerModeCapture = argumentCaptor<Int>()
         whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
@@ -127,7 +127,7 @@
     }
 
     @Test
-    fun `triggered - state is not SILENT - move to SILENT ringer`() = testScope.runTest {
+    fun triggered_stateIsNotSILENT_moveToSILENTringer() = testScope.runTest {
         //given
         val ringerModeCapture = argumentCaptor<Int>()
         whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
index facc747..f243d7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
@@ -101,7 +101,7 @@
     }
 
     @Test
-    fun `feature flag is OFF - do nothing with keyguardQuickAffordanceRepository`() = testScope.runTest {
+    fun featureFlagIsOFF_doNothingWithKeyguardQuickAffordanceRepository() = testScope.runTest {
         //given
         whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(false)
 
@@ -114,7 +114,7 @@
     }
 
     @Test
-    fun `feature flag is ON - call to keyguardQuickAffordanceRepository`() = testScope.runTest {
+    fun featureFlagIsON_callToKeyguardQuickAffordanceRepository() = testScope.runTest {
         //given
         val ringerModeInternal = mock<MutableLiveData<Int>>()
         whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
@@ -129,7 +129,7 @@
     }
 
     @Test
-    fun `ringer mode is changed to SILENT - do not save to shared preferences`() = testScope.runTest {
+    fun ringerModeIsChangedToSILENT_doNotSaveToSharedPreferences() = testScope.runTest {
         //given
         val ringerModeInternal = mock<MutableLiveData<Int>>()
         val observerCaptor = argumentCaptor<Observer<Int>>()
@@ -147,7 +147,7 @@
     }
 
     @Test
-    fun `ringerModeInternal changes to something not SILENT - is set in sharedpreferences`() = testScope.runTest {
+    fun ringerModeInternalChangesToSomethingNotSILENT_isSetInSharedpreferences() = testScope.runTest {
         //given
         val newRingerMode = 99
         val observerCaptor = argumentCaptor<Observer<Int>>()
@@ -172,7 +172,7 @@
     }
 
     @Test
-    fun `MUTE is in selections - observe ringerModeInternal`() = testScope.runTest {
+    fun MUTEisInSelections_observeRingerModeInternal() = testScope.runTest {
         //given
         val ringerModeInternal = mock<MutableLiveData<Int>>()
         whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
@@ -187,7 +187,7 @@
     }
 
     @Test
-    fun `MUTE is in selections 2x - observe ringerModeInternal`() = testScope.runTest {
+    fun MUTEisInSelections2x_observeRingerModeInternal() = testScope.runTest {
         //given
         val config: KeyguardQuickAffordanceConfig = mock()
         whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE)
@@ -206,7 +206,7 @@
     }
 
     @Test
-    fun `MUTE is not in selections - stop observing ringerModeInternal`() = testScope.runTest {
+    fun MUTEisNotInSelections_stopObservingRingerModeInternal() = testScope.runTest {
         //given
         val config: KeyguardQuickAffordanceConfig = mock()
         whenever(config.key).thenReturn("notmutequickaffordance")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 1adf808..faf18d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -55,7 +55,7 @@
     }
 
     @Test
-    fun `affordance - sets up registration and delivers initial model`() = runBlockingTest {
+    fun affordance_setsUpRegistrationAndDeliversInitialModel() = runBlockingTest {
         whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
@@ -75,7 +75,7 @@
     }
 
     @Test
-    fun `affordance - scanner activity changed - delivers model with updated intent`() =
+    fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() =
         runBlockingTest {
             whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
             var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -93,7 +93,7 @@
         }
 
     @Test
-    fun `affordance - scanner preference changed - delivers visible model`() = runBlockingTest {
+    fun affordance_scannerPreferenceChanged_deliversVisibleModel() = runBlockingTest {
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
         val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
         val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
@@ -109,7 +109,7 @@
     }
 
     @Test
-    fun `affordance - scanner preference changed - delivers none`() = runBlockingTest {
+    fun affordance_scannerPreferenceChanged_deliversNone() = runBlockingTest {
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
         val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
         val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
@@ -136,7 +136,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - enabled if configured on device - can open camera`() = runTest {
+    fun getPickerScreenState_enabledIfConfiguredOnDevice_canOpenCamera() = runTest {
         whenever(controller.isAvailableOnDevice).thenReturn(true)
         whenever(controller.isAbleToOpenCameraApp).thenReturn(true)
 
@@ -145,7 +145,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - disabled if configured on device - cannot open camera`() = runTest {
+    fun getPickerScreenState_disabledIfConfiguredOnDevice_cannotOpenCamera() = runTest {
         whenever(controller.isAvailableOnDevice).thenReturn(true)
         whenever(controller.isAbleToOpenCameraApp).thenReturn(false)
 
@@ -154,7 +154,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - unavailable if not configured on device`() = runTest {
+    fun getPickerScreenState_unavailableIfNotConfiguredOnDevice() = runTest {
         whenever(controller.isAvailableOnDevice).thenReturn(false)
         whenever(controller.isAbleToOpenCameraApp).thenReturn(true)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 752963f..952882d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -69,7 +69,7 @@
     }
 
     @Test
-    fun `affordance - keyguard showing - has wallet card - visible model`() = runBlockingTest {
+    fun affordance_keyguardShowing_hasWalletCard_visibleModel() = runBlockingTest {
         setUpState()
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
@@ -90,7 +90,7 @@
     }
 
     @Test
-    fun `affordance - wallet not enabled - model is none`() = runBlockingTest {
+    fun affordance_walletNotEnabled_modelIsNone() = runBlockingTest {
         setUpState(isWalletEnabled = false)
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
@@ -102,7 +102,7 @@
     }
 
     @Test
-    fun `affordance - query not successful - model is none`() = runBlockingTest {
+    fun affordance_queryNotSuccessful_modelIsNone() = runBlockingTest {
         setUpState(isWalletQuerySuccessful = false)
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
@@ -114,7 +114,7 @@
     }
 
     @Test
-    fun `affordance - no selected card - model is none`() = runBlockingTest {
+    fun affordance_noSelectedCard_modelIsNone() = runBlockingTest {
         setUpState(hasSelectedCard = false)
         var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
@@ -143,7 +143,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - default`() = runTest {
+    fun getPickerScreenState_default() = runTest {
         setUpState()
 
         assertThat(underTest.getPickerScreenState())
@@ -151,7 +151,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - unavailable`() = runTest {
+    fun getPickerScreenState_unavailable() = runTest {
         setUpState(
             isWalletServiceAvailable = false,
         )
@@ -161,7 +161,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - disabled when the feature is not enabled`() = runTest {
+    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest {
         setUpState(
             isWalletEnabled = false,
         )
@@ -171,7 +171,7 @@
     }
 
     @Test
-    fun `getPickerScreenState - disabled when there is no card`() = runTest {
+    fun getPickerScreenState_disabledWhenThereIsNoCard() = runTest {
         setUpState(
             hasSelectedCard = false,
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index f1b9c5f..a9b9c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -73,7 +73,7 @@
     }
 
     @Test
-    fun `lockScreenState - visible when launchable`() =
+    fun lockScreenState_visibleWhenLaunchable() =
         testScope.runTest {
             setLaunchable()
 
@@ -84,7 +84,7 @@
         }
 
     @Test
-    fun `lockScreenState - hidden when app not installed on device`() =
+    fun lockScreenState_hiddenWhenAppNotInstalledOnDevice() =
         testScope.runTest {
             setLaunchable(isVideoCameraAppInstalled = false)
 
@@ -95,7 +95,7 @@
         }
 
     @Test
-    fun `lockScreenState - hidden when camera disabled by admin`() =
+    fun lockScreenState_hiddenWhenCameraDisabledByAdmin() =
         testScope.runTest {
             setLaunchable(isCameraDisabledByAdmin = true)
 
@@ -106,7 +106,7 @@
         }
 
     @Test
-    fun `getPickerScreenState - default when launchable`() =
+    fun getPickerScreenState_defaultWhenLaunchable() =
         testScope.runTest {
             setLaunchable()
 
@@ -115,7 +115,7 @@
         }
 
     @Test
-    fun `getPickerScreenState - unavailable when app not installed on device`() =
+    fun getPickerScreenState_unavailableWhenAppNotInstalledOnDevice() =
         testScope.runTest {
             setLaunchable(isVideoCameraAppInstalled = false)
 
@@ -124,7 +124,7 @@
         }
 
     @Test
-    fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+    fun getPickerScreenState_unavailableWhenCameraDisabledByAdmin() =
         testScope.runTest {
             setLaunchable(isCameraDisabledByAdmin = true)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 5d83f56..726728a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -227,10 +227,14 @@
 
             assertThat(faceEnrolled()).isFalse()
 
+            whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
+
             enrollmentChange(FACE, ANOTHER_USER_ID, true)
 
             assertThat(faceEnrolled()).isFalse()
 
+            whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(true)
+
             enrollmentChange(FACE, PRIMARY_USER_ID, true)
 
             assertThat(faceEnrolled()).isTrue()
@@ -264,6 +268,7 @@
 
             verify(authController).addCallback(authControllerCallback.capture())
 
+            whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
             enrollmentChange(FACE, ANOTHER_USER_ID, true)
 
             assertThat(faceEnrolled()).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index fc75d47..a76d03b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.content.pm.UserInfo
 import android.content.pm.UserInfo.FLAG_PRIMARY
 import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE
 import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
 import android.hardware.biometrics.ComponentInfoInternal
 import android.hardware.face.FaceAuthenticateOptions
@@ -824,6 +825,26 @@
             verify(faceManager).scheduleWatchdog()
         }
 
+    @Test
+    fun retryFaceIfThereIsAHardwareError() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(fallbackToDetect = false)
+            clearInvocations(faceManager)
+
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_HW_UNAVAILABLE,
+                "HW unavailable"
+            )
+
+            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.HAL_ERROR_RETRY_TIMEOUT)
+            runCurrent()
+
+            faceAuthenticateIsCalled()
+        }
+
     private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
         initCollectors()
         allPreconditionsToRunFaceAuthAreTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index a668af3..12b8261 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -258,7 +258,7 @@
     }
 
     @Test
-    fun `selections for secondary user`() =
+    fun selectionsForSecondaryUser() =
         testScope.runTest {
             userTracker.set(
                 userInfos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 3fd97da..b53a434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -281,7 +281,7 @@
         }
 
     @Test
-    fun `isDozing - starts with correct initial value for isDozing`() =
+    fun isDozing_startsWithCorrectInitialValueForIsDozing() =
         testScope.runTest {
             var latest: Boolean? = null
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index d9d4013..d0bfaa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -70,7 +70,7 @@
     }
 
     @Test
-    fun `startTransition runs animator to completion`() =
+    fun startTransitionRunsAnimatorToCompletion() =
         TestScope().runTest {
             val steps = mutableListOf<TransitionStep>()
             val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -86,7 +86,7 @@
         }
 
     @Test
-    fun `starting second transition will cancel the first transition`() =
+    fun startingSecondTransitionWillCancelTheFirstTransition() =
         TestScope().runTest {
             val steps = mutableListOf<TransitionStep>()
             val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -114,7 +114,7 @@
         }
 
     @Test
-    fun `Null animator enables manual control with updateTransition`() =
+    fun nullAnimatorEnablesManualControlWithUpdateTransition() =
         TestScope().runTest {
             val steps = mutableListOf<TransitionStep>()
             val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -146,13 +146,13 @@
         }
 
     @Test
-    fun `Attempt to  manually update transition with invalid UUID throws exception`() {
+    fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() {
         underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
         assertThat(wtfHandler.failed).isTrue()
     }
 
     @Test
-    fun `Attempt to manually update transition after FINISHED state throws exception`() {
+    fun attemptToManuallyUpdateTransitionAfterFINISHEDstateThrowsException() {
         val uuid =
             underTest.startTransition(
                 TransitionInfo(
@@ -171,7 +171,7 @@
     }
 
     @Test
-    fun `Attempt to manually update transition after CANCELED state throws exception`() {
+    fun attemptToManuallyUpdateTransitionAfterCANCELEDstateThrowsException() {
         val uuid =
             underTest.startTransition(
                 TransitionInfo(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index f9493d1..9daf3f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -48,7 +48,7 @@
     }
 
     @Test
-    fun `nextRevealEffect - effect switches between default and biometric with no dupes`() =
+    fun nextRevealEffect_effectSwitchesBetweenDefaultAndBiometricWithNoDupes() =
         runTest {
             val values = mutableListOf<LightRevealEffect>()
             val job = launch { underTest.revealEffect.collect { values.add(it) } }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 77bb12c..8a0cf4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -92,7 +92,7 @@
         }
 
     @Test
-    fun `isEnabled - always false when quick settings are visible`() =
+    fun isEnabled_alwaysFalseWhenQuickSettingsAreVisible() =
         testScope.runTest {
             val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
             KeyguardState.values().forEach { keyguardState ->
@@ -163,7 +163,7 @@
         }
 
     @Test
-    fun `long pressed - close dialogs broadcast received - popup dismissed`() =
+    fun longPressed_closeDialogsBroadcastReceived_popupDismissed() =
         testScope.runTest {
             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
             runCurrent()
@@ -211,7 +211,7 @@
         }
 
     @Test
-    fun `logs when menu is shown`() =
+    fun logsWhenMenuIsShown() =
         testScope.runTest {
             collectLastValue(underTest.isMenuVisible)
             runCurrent()
@@ -223,7 +223,7 @@
         }
 
     @Test
-    fun `logs when menu is clicked`() =
+    fun logsWhenMenuIsClicked() =
         testScope.runTest {
             collectLastValue(underTest.isMenuVisible)
             runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 503e002..96fff64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -195,7 +195,7 @@
     }
 
     @Test
-    fun `quickAffordance - bottom start affordance is visible`() =
+    fun quickAffordance_bottomStartAffordanceIsVisible() =
         testScope.runTest {
             val configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
             homeControls.setState(
@@ -221,7 +221,7 @@
         }
 
     @Test
-    fun `quickAffordance - bottom end affordance is visible`() =
+    fun quickAffordance_bottomEndAffordanceIsVisible() =
         testScope.runTest {
             val configKey = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
             quickAccessWallet.setState(
@@ -246,7 +246,7 @@
         }
 
     @Test
-    fun `quickAffordance - hidden when all features are disabled by device policy`() =
+    fun quickAffordance_hiddenWhenAllFeaturesAreDisabledByDevicePolicy() =
         testScope.runTest {
             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
@@ -265,7 +265,7 @@
         }
 
     @Test
-    fun `quickAffordance - hidden when shortcuts feature is disabled by device policy`() =
+    fun quickAffordance_hiddenWhenShortcutsFeatureIsDisabledByDevicePolicy() =
         testScope.runTest {
             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
@@ -284,7 +284,7 @@
         }
 
     @Test
-    fun `quickAffordance - hidden when quick settings is visible`() =
+    fun quickAffordance_hiddenWhenQuickSettingsIsVisible() =
         testScope.runTest {
             repository.setQuickSettingsVisible(true)
             quickAccessWallet.setState(
@@ -302,7 +302,7 @@
         }
 
     @Test
-    fun `quickAffordance - bottom start affordance hidden while dozing`() =
+    fun quickAffordance_bottomStartAffordanceHiddenWhileDozing() =
         testScope.runTest {
             repository.setDozing(true)
             homeControls.setState(
@@ -319,7 +319,7 @@
         }
 
     @Test
-    fun `quickAffordance - bottom start affordance hidden when lockscreen is not showing`() =
+    fun quickAffordance_bottomStartAffordanceHiddenWhenLockscreenIsNotShowing() =
         testScope.runTest {
             repository.setKeyguardShowing(false)
             homeControls.setState(
@@ -336,7 +336,7 @@
         }
 
     @Test
-    fun `quickAffordanceAlwaysVisible - even when lock screen not showing and dozing`() =
+    fun quickAffordanceAlwaysVisible_evenWhenLockScreenNotShowingAndDozing() =
         testScope.runTest {
             repository.setKeyguardShowing(false)
             repository.setDozing(true)
@@ -511,7 +511,7 @@
         }
 
     @Test
-    fun `unselect - one`() =
+    fun unselect_one() =
         testScope.runTest {
             featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
             homeControls.setState(
@@ -588,7 +588,7 @@
         }
 
     @Test
-    fun `unselect - all`() =
+    fun unselect_all() =
         testScope.runTest {
             featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
             homeControls.setState(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 276b3e3..503687d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -52,7 +52,7 @@
     }
 
     @Test
-    fun `transition collectors receives only appropriate events`() = runTest {
+    fun transitionCollectorsReceivesOnlyAppropriateEvents() = runTest {
         val lockscreenToAodSteps by collectValues(underTest.lockscreenToAodTransition)
         val aodToLockscreenSteps by collectValues(underTest.aodToLockscreenTransition)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index e2d0ec3..fe65236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -180,7 +180,7 @@
     }
 
     @Test
-    fun `DREAMING to LOCKSCREEN`() =
+    fun DREAMINGtoLOCKSCREEN() =
         testScope.runTest {
             // GIVEN a device is dreaming
             keyguardRepository.setDreamingWithOverlay(true)
@@ -215,7 +215,7 @@
         }
 
     @Test
-    fun `LOCKSCREEN to PRIMARY_BOUNCER via bouncer showing call`() =
+    fun LOCKSCREENtoPRIMARY_BOUNCERviaBouncerShowingCall() =
         testScope.runTest {
             // GIVEN a device that has at least woken up
             keyguardRepository.setWakefulnessModel(startingToWake())
@@ -242,7 +242,7 @@
         }
 
     @Test
-    fun `OCCLUDED to DOZING`() =
+    fun OCCLUDEDtoDOZING() =
         testScope.runTest {
             // GIVEN a device with AOD not available
             keyguardRepository.setAodAvailable(false)
@@ -269,7 +269,7 @@
         }
 
     @Test
-    fun `OCCLUDED to AOD`() =
+    fun OCCLUDEDtoAOD() =
         testScope.runTest {
             // GIVEN a device with AOD available
             keyguardRepository.setAodAvailable(true)
@@ -296,7 +296,7 @@
         }
 
     @Test
-    fun `LOCKSCREEN to DREAMING`() =
+    fun LOCKSCREENtoDREAMING() =
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
             keyguardRepository.setDreamingWithOverlay(false)
@@ -327,7 +327,7 @@
         }
 
     @Test
-    fun `LOCKSCREEN to DOZING`() =
+    fun LOCKSCREENtoDOZING() =
         testScope.runTest {
             // GIVEN a device with AOD not available
             keyguardRepository.setAodAvailable(false)
@@ -354,7 +354,7 @@
         }
 
     @Test
-    fun `LOCKSCREEN to AOD`() =
+    fun LOCKSCREENtoAOD() =
         testScope.runTest {
             // GIVEN a device with AOD available
             keyguardRepository.setAodAvailable(true)
@@ -381,7 +381,7 @@
         }
 
     @Test
-    fun `DOZING to LOCKSCREEN`() =
+    fun DOZINGtoLOCKSCREEN() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
             runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
@@ -404,7 +404,7 @@
         }
 
     @Test
-    fun `DOZING to LOCKSCREEN cannot be interruped by DREAMING`() =
+    fun DOZINGtoLOCKSCREENcannotBeInterrupedByDREAMING() =
         testScope.runTest {
             // GIVEN a prior transition has started to LOCKSCREEN
             transitionRepository.sendTransitionStep(
@@ -430,7 +430,7 @@
         }
 
     @Test
-    fun `DOZING to GONE`() =
+    fun DOZINGtoGONE() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
             runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
@@ -453,7 +453,7 @@
         }
 
     @Test
-    fun `GONE to DOZING`() =
+    fun GONEtoDOZING() =
         testScope.runTest {
             // GIVEN a device with AOD not available
             keyguardRepository.setAodAvailable(false)
@@ -480,7 +480,7 @@
         }
 
     @Test
-    fun `GONE to AOD`() =
+    fun GONEtoAOD() =
         testScope.runTest {
             // GIVEN a device with AOD available
             keyguardRepository.setAodAvailable(true)
@@ -507,7 +507,7 @@
         }
 
     @Test
-    fun `GONE to LOCKSREEN`() =
+    fun GONEtoLOCKSREEN() =
         testScope.runTest {
             // GIVEN a prior transition has run to GONE
             runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
@@ -530,7 +530,7 @@
         }
 
     @Test
-    fun `GONE to DREAMING`() =
+    fun GONEtoDREAMING() =
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
             keyguardRepository.setDreamingWithOverlay(false)
@@ -561,7 +561,7 @@
         }
 
     @Test
-    fun `ALTERNATE_BOUNCER to PRIMARY_BOUNCER`() =
+    fun ALTERNATE_BOUNCERtoPRIMARY_BOUNCER() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
             runTransition(KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
@@ -584,7 +584,7 @@
         }
 
     @Test
-    fun `ALTERNATE_BOUNCER to AOD`() =
+    fun ALTERNATE_BOUNCERtoAOD() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
             bouncerRepository.setAlternateVisible(true)
@@ -613,7 +613,7 @@
         }
 
     @Test
-    fun `ALTERNATE_BOUNCER to DOZING`() =
+    fun ALTERNATE_BOUNCERtoDOZING() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
             bouncerRepository.setAlternateVisible(true)
@@ -643,7 +643,7 @@
         }
 
     @Test
-    fun `ALTERNATE_BOUNCER to LOCKSCREEN`() =
+    fun ALTERNATE_BOUNCERtoLOCKSCREEN() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
             bouncerRepository.setAlternateVisible(true)
@@ -671,7 +671,7 @@
         }
 
     @Test
-    fun `PRIMARY_BOUNCER to AOD`() =
+    fun PRIMARY_BOUNCERtoAOD() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
@@ -699,7 +699,7 @@
         }
 
     @Test
-    fun `PRIMARY_BOUNCER to DOZING`() =
+    fun PRIMARY_BOUNCERtoDOZING() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
@@ -727,7 +727,7 @@
         }
 
     @Test
-    fun `PRIMARY_BOUNCER to LOCKSCREEN`() =
+    fun PRIMARY_BOUNCERtoLOCKSCREEN() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
@@ -754,7 +754,7 @@
         }
 
     @Test
-    fun `OCCLUDED to GONE`() =
+    fun OCCLUDEDtoGONE() =
         testScope.runTest {
             // GIVEN a device on lockscreen
             keyguardRepository.setKeyguardShowing(true)
@@ -785,7 +785,7 @@
         }
 
     @Test
-    fun `OCCLUDED to LOCKSCREEN`() =
+    fun OCCLUDEDtoLOCKSCREEN() =
         testScope.runTest {
             // GIVEN a device on lockscreen
             keyguardRepository.setKeyguardShowing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 6236616..359854b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -69,7 +69,7 @@
     }
 
     @Test
-    fun `lightRevealEffect - does not change during keyguard transition`() =
+    fun lightRevealEffect_doesNotChangeDuringKeyguardTransition() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<LightRevealEffect>()
             val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
@@ -103,7 +103,7 @@
         }
 
     @Test
-    fun `revealAmount - inverted when appropriate`() =
+    fun revealAmount_invertedWhenAppropriate() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<Float>()
             val job = underTest.revealAmount.onEach(values::add).launchIn(this)
@@ -132,7 +132,7 @@
         }
 
     @Test
-    fun `revealAmount - ignores transitions that do not affect reveal amount`() =
+    fun revealAmount_ignoresTransitionsThatDoNotAffectRevealAmount() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<Float>()
             val job = underTest.revealAmount.onEach(values::add).launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 224eec1..2361c59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -251,7 +251,7 @@
     }
 
     @Test
-    fun `startButton - present - visible model - starts activity on click`() =
+    fun startButton_present_visibleModel_startsActivityOnClick() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.startButton)
@@ -280,7 +280,7 @@
         }
 
     @Test
-    fun `startButton - hidden when device policy disables all keyguard features`() =
+    fun startButton_hiddenWhenDevicePolicyDisablesAllKeyguardFeatures() =
         testScope.runTest {
             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
@@ -315,7 +315,7 @@
         }
 
     @Test
-    fun `startButton - in preview mode - visible even when keyguard not showing`() =
+    fun startButton_inPreviewMode_visibleEvenWhenKeyguardNotShowing() =
         testScope.runTest {
             underTest.enablePreviewMode(
                 initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -359,7 +359,7 @@
         }
 
     @Test
-    fun `endButton - in higlighted preview mode - dimmed when other is selected`() =
+    fun endButton_inHiglightedPreviewMode_dimmedWhenOtherIsSelected() =
         testScope.runTest {
             underTest.enablePreviewMode(
                 initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -416,7 +416,7 @@
         }
 
     @Test
-    fun `endButton - present - visible model - do nothing on click`() =
+    fun endButton_present_visibleModel_doNothingOnClick() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.endButton)
@@ -445,7 +445,7 @@
         }
 
     @Test
-    fun `startButton - not present - model is hidden`() =
+    fun startButton_notPresent_modelIsHidden() =
         testScope.runTest {
             val latest = collectLastValue(underTest.startButton)
 
@@ -524,7 +524,7 @@
         }
 
     @Test
-    fun `alpha - in preview mode - does not change`() =
+    fun alpha_inPreviewMode_doesNotChange() =
         testScope.runTest {
             underTest.enablePreviewMode(
                 initiallySelectedSlotId = null,
@@ -629,7 +629,7 @@
         }
 
     @Test
-    fun `isClickable - true when alpha at threshold`() =
+    fun isClickable_trueWhenAlphaAtThreshold() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
             repository.setBottomAreaAlpha(
@@ -661,7 +661,7 @@
         }
 
     @Test
-    fun `isClickable - true when alpha above threshold`() =
+    fun isClickable_trueWhenAlphaAboveThreshold() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.startButton)
@@ -692,7 +692,7 @@
         }
 
     @Test
-    fun `isClickable - false when alpha below threshold`() =
+    fun isClickable_falseWhenAlphaBelowThreshold() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.startButton)
@@ -723,7 +723,7 @@
         }
 
     @Test
-    fun `isClickable - false when alpha at zero`() =
+    fun isClickable_falseWhenAlphaAtZero() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.startButton)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index ea11f01..afab250 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -88,7 +88,7 @@
     }
 
     @Test(expected = IllegalStateException::class)
-    fun `repeatWhenAttached - enforces main thread`() =
+    fun repeatWhenAttached_enforcesMainThread() =
         testScope.runTest {
             Assert.setTestThread(null)
 
@@ -96,7 +96,7 @@
         }
 
     @Test(expected = IllegalStateException::class)
-    fun `repeatWhenAttached - dispose enforces main thread`() =
+    fun repeatWhenAttached_disposeEnforcesMainThread() =
         testScope.runTest {
             val disposableHandle = repeatWhenAttached()
             Assert.setTestThread(null)
@@ -105,7 +105,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - view starts detached - runs block when attached`() =
+    fun repeatWhenAttached_viewStartsDetached_runsBlockWhenAttached() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(false)
             repeatWhenAttached()
@@ -120,7 +120,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - view already attached - immediately runs block`() =
+    fun repeatWhenAttached_viewAlreadyAttached_immediatelyRunsBlock() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
 
@@ -132,7 +132,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - starts visible without focus - STARTED`() =
+    fun repeatWhenAttached_startsVisibleWithoutFocus_STARTED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             whenever(view.windowVisibility).thenReturn(View.VISIBLE)
@@ -145,7 +145,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - starts with focus but invisible - CREATED`() =
+    fun repeatWhenAttached_startsWithFocusButInvisible_CREATED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             whenever(view.hasWindowFocus()).thenReturn(true)
@@ -158,7 +158,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - starts visible and with focus - RESUMED`() =
+    fun repeatWhenAttached_startsVisibleAndWithFocus_RESUMED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             whenever(view.windowVisibility).thenReturn(View.VISIBLE)
@@ -172,7 +172,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - becomes visible without focus - STARTED`() =
+    fun repeatWhenAttached_becomesVisibleWithoutFocus_STARTED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             repeatWhenAttached()
@@ -188,7 +188,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - gains focus but invisible - CREATED`() =
+    fun repeatWhenAttached_gainsFocusButInvisible_CREATED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             repeatWhenAttached()
@@ -204,7 +204,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - becomes visible and gains focus - RESUMED`() =
+    fun repeatWhenAttached_becomesVisibleAndGainsFocus_RESUMED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             repeatWhenAttached()
@@ -224,7 +224,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - view gets detached - destroys the lifecycle`() =
+    fun repeatWhenAttached_viewGetsDetached_destroysTheLifecycle() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             repeatWhenAttached()
@@ -238,7 +238,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - view gets reattached - recreates a lifecycle`() =
+    fun repeatWhenAttached_viewGetsReattached_recreatesAlifecycle() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             repeatWhenAttached()
@@ -255,7 +255,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - dispose attached`() =
+    fun repeatWhenAttached_disposeAttached() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             val handle = repeatWhenAttached()
@@ -269,7 +269,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - dispose never attached`() =
+    fun repeatWhenAttached_disposeNeverAttached() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(false)
             val handle = repeatWhenAttached()
@@ -281,7 +281,7 @@
         }
 
     @Test
-    fun `repeatWhenAttached - dispose previously attached now detached`() =
+    fun repeatWhenAttached_disposePreviouslyAttachedNowDetached() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
             val handle = repeatWhenAttached()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
index 411b1bd..af83a56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
@@ -31,7 +31,7 @@
     private val underTest = TableLogBufferFactory(dumpManager, systemClock)
 
     @Test
-    fun `create - always creates new instance`() {
+    fun create_alwaysCreatesNewInstance() {
         val b1 = underTest.create(NAME_1, SIZE)
         val b1_copy = underTest.create(NAME_1, SIZE)
         val b2 = underTest.create(NAME_2, SIZE)
@@ -43,7 +43,7 @@
     }
 
     @Test
-    fun `getOrCreate - reuses instance`() {
+    fun getOrCreate_reusesInstance() {
         val b1 = underTest.getOrCreate(NAME_1, SIZE)
         val b1_copy = underTest.getOrCreate(NAME_1, SIZE)
         val b2 = underTest.getOrCreate(NAME_2, SIZE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
index 56c91bc..e3c8b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
@@ -22,6 +22,7 @@
 import android.media.session.PlaybackState
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.MotionEvent
 import android.widget.SeekBar
 import androidx.arch.core.executor.ArchTaskExecutor
 import androidx.arch.core.executor.TaskExecutor
@@ -466,21 +467,63 @@
         whenever(mockController.getTransportControls()).thenReturn(mockTransport)
         whenever(falsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).thenReturn(true)
         whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
-        viewModel.updateController(mockController)
-        val pos = 169
 
-        viewModel.attachTouchHandlers(mockBar)
+        viewModel.updateController(mockController)
+        val pos = 40
+        val bar = SeekBar(context).apply { progress = pos }
         with(viewModel.seekBarListener) {
-            onStartTrackingTouch(mockBar)
-            onProgressChanged(mockBar, pos, true)
-            onStopTrackingTouch(mockBar)
+            onStartTrackingTouch(bar)
+            onStopTrackingTouch(bar)
         }
+        fakeExecutor.runAllReady()
 
         // THEN transport controls should not be used
         verify(mockTransport, never()).seekTo(pos.toLong())
     }
 
     @Test
+    fun onSeekbarGrabInvalidTouch() {
+        whenever(mockController.getTransportControls()).thenReturn(mockTransport)
+        viewModel.firstMotionEvent =
+            MotionEvent.obtain(12L, 13L, MotionEvent.ACTION_DOWN, 76F, 0F, 0)
+        viewModel.lastMotionEvent = MotionEvent.obtain(12L, 14L, MotionEvent.ACTION_UP, 78F, 4F, 0)
+        val pos = 78
+
+        viewModel.updateController(mockController)
+        // WHEN user ends drag
+        val bar = SeekBar(context).apply { progress = pos }
+        with(viewModel.seekBarListener) {
+            onStartTrackingTouch(bar)
+            onStopTrackingTouch(bar)
+        }
+        fakeExecutor.runAllReady()
+
+        // THEN transport controls should not be used
+        verify(mockTransport, never()).seekTo(pos.toLong())
+    }
+
+    @Test
+    fun onSeekbarGrabValidTouch() {
+        whenever(mockController.transportControls).thenReturn(mockTransport)
+        viewModel.firstMotionEvent =
+            MotionEvent.obtain(12L, 13L, MotionEvent.ACTION_DOWN, 36F, 0F, 0)
+        viewModel.lastMotionEvent = MotionEvent.obtain(12L, 14L, MotionEvent.ACTION_UP, 40F, 1F, 0)
+        val pos = 40
+
+        viewModel.updateController(mockController)
+        // WHEN user ends drag
+        val bar = SeekBar(context).apply { progress = pos }
+        with(viewModel.seekBarListener) {
+            onStartTrackingTouch(bar)
+            onStopTrackingTouch(bar)
+        }
+        fakeExecutor.runAllReady()
+
+        // THEN transport controls should be used
+        verify(mockTransport).seekTo(pos.toLong())
+    }
+
+    @Test
     fun queuePollTaskWhenPlaying() {
         // GIVEN that the track is playing
         val state =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index 4565762..c9956f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -24,6 +24,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.TransitionLayout
@@ -55,13 +57,12 @@
     @Mock private lateinit var mockCopiedState: TransitionViewState
     @Mock private lateinit var detailWidgetState: WidgetState
     @Mock private lateinit var controlWidgetState: WidgetState
-    @Mock private lateinit var bgWidgetState: WidgetState
     @Mock private lateinit var mediaTitleWidgetState: WidgetState
     @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
     @Mock private lateinit var mediaContainerWidgetState: WidgetState
     @Mock private lateinit var mediaFlags: MediaFlags
 
-    val delta = 0.1F
+    private val delta = 0.1F
 
     private lateinit var mediaViewController: MediaViewController
 
@@ -84,13 +85,13 @@
 
         mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
         // Change the height to see the effect of orientation change.
-        MediaViewController.backgroundIds.forEach { id ->
+        MediaViewHolder.backgroundIds.forEach { id ->
             mediaViewController.expandedLayout.getConstraint(id).layout.mHeight = 10
         }
         newConfig.orientation = ORIENTATION_LANDSCAPE
         configurationController.onConfigurationChanged(newConfig)
 
-        MediaViewController.backgroundIds.forEach { id ->
+        MediaViewHolder.backgroundIds.forEach { id ->
             assertTrue(
                 mediaViewController.expandedLayout.getConstraint(id).layout.mHeight ==
                     context.resources.getDimensionPixelSize(
@@ -107,7 +108,7 @@
         mediaViewController.attach(recommendation, MediaViewController.TYPE.RECOMMENDATION)
         // Change the height to see the effect of orientation change.
         mediaViewController.expandedLayout
-            .getConstraint(MediaViewController.recSizingViewId)
+            .getConstraint(RecommendationViewHolder.backgroundId)
             .layout
             .mHeight = 10
         newConfig.orientation = ORIENTATION_LANDSCAPE
@@ -115,7 +116,7 @@
 
         assertTrue(
             mediaViewController.expandedLayout
-                .getConstraint(MediaViewController.recSizingViewId)
+                .getConstraint(RecommendationViewHolder.backgroundId)
                 .layout
                 .mHeight ==
                 context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index c3fabfe..21a7a34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -39,7 +39,7 @@
 import android.util.FeatureFlagUtils;
 import android.view.View;
 
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -65,7 +65,7 @@
 import java.util.List;
 import java.util.Optional;
 
-@SmallTest
+@MediumTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class MediaOutputDialogTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 22a5b21..7dc622b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -36,6 +36,7 @@
 import android.content.pm.ShortcutInfo
 import android.content.pm.ShortcutManager
 import android.content.pm.UserInfo
+import android.graphics.drawable.Icon
 import android.os.UserHandle
 import android.os.UserManager
 import androidx.test.ext.truth.content.IntentSubject.assertThat
@@ -64,7 +65,6 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.isNull
 import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
@@ -98,7 +98,7 @@
         whenever(context.getString(R.string.note_task_button_label))
             .thenReturn(NOTE_TASK_SHORT_LABEL)
         whenever(context.packageManager).thenReturn(packageManager)
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(NOTE_TASK_INFO)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(NOTE_TASK_INFO)
         whenever(userManager.isUserUnlocked).thenReturn(true)
         whenever(
                 devicePolicyManager.getKeyguardDisabledFeatures(
@@ -142,7 +142,7 @@
             .apply { infoReference.set(expectedInfo) }
             .onBubbleExpandChanged(
                 isExpanding = true,
-                key = Bubble.KEY_APP_BUBBLE,
+                key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
             )
 
         verify(eventLogger).logNoteTaskOpened(expectedInfo)
@@ -157,7 +157,7 @@
             .apply { infoReference.set(expectedInfo) }
             .onBubbleExpandChanged(
                 isExpanding = false,
-                key = Bubble.KEY_APP_BUBBLE,
+                key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
             )
 
         verify(eventLogger).logNoteTaskClosed(expectedInfo)
@@ -172,7 +172,7 @@
             .apply { infoReference.set(expectedInfo) }
             .onBubbleExpandChanged(
                 isExpanding = true,
-                key = Bubble.KEY_APP_BUBBLE,
+                key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
             )
 
         verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
@@ -186,7 +186,7 @@
             .apply { infoReference.set(expectedInfo) }
             .onBubbleExpandChanged(
                 isExpanding = false,
-                key = Bubble.KEY_APP_BUBBLE,
+                key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
             )
 
         verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
@@ -208,7 +208,7 @@
         createNoteTaskController(isEnabled = false)
             .onBubbleExpandChanged(
                 isExpanding = true,
-                key = Bubble.KEY_APP_BUBBLE,
+                key = Bubble.getAppBubbleKeyForApp(NOTE_TASK_INFO.packageName, NOTE_TASK_INFO.user),
             )
 
         verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
@@ -224,7 +224,7 @@
                 isKeyguardLocked = true,
             )
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
 
         createNoteTaskController()
             .showNoteTask(
@@ -256,9 +256,10 @@
             NOTE_TASK_INFO.copy(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
                 isKeyguardLocked = true,
+                user = user10,
             )
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
 
         createNoteTaskController()
             .showNoteTaskAsUser(
@@ -292,7 +293,7 @@
                 isKeyguardLocked = true,
             )
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
         whenever(activityManager.getRunningTasks(anyInt()))
             .thenReturn(listOf(NOTE_RUNNING_TASK_INFO))
 
@@ -318,7 +319,7 @@
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
                 isKeyguardLocked = false,
             )
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
 
         createNoteTaskController()
@@ -326,7 +327,8 @@
                 entryPoint = expectedInfo.entryPoint!!,
             )
 
-        verifyZeroInteractions(context)
+        // Context package name used to create bubble icon from drawable resource id
+        verify(context).packageName
         verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
         verifyZeroInteractions(eventLogger)
     }
@@ -343,7 +345,7 @@
 
     @Test
     fun showNoteTask_intentResolverReturnsNull_shouldShowToast() {
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(null)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
         val noteTaskController = spy(createNoteTaskController())
         doNothing().whenever(noteTaskController).showNoDefaultNotesAppToast()
 
@@ -383,7 +385,7 @@
                 isKeyguardLocked = true,
             )
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
 
         createNoteTaskController()
             .showNoteTask(
@@ -603,14 +605,19 @@
 
     private fun verifyNoteTaskOpenInBubbleInUser(userHandle: UserHandle) {
         val intentCaptor = argumentCaptor<Intent>()
+        val iconCaptor = argumentCaptor<Icon>()
         verify(bubbles)
-            .showOrHideAppBubble(capture(intentCaptor), eq(userHandle), /* icon = */ isNull())
+            .showOrHideAppBubble(capture(intentCaptor), eq(userHandle), capture(iconCaptor))
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
             assertThat(intent.flags).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
             assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
         }
+        iconCaptor.value.let { icon ->
+            assertThat(icon).isNotNull()
+            assertThat(icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
+        }
     }
 
     // region updateNoteTaskAsUser
@@ -711,6 +718,7 @@
             NoteTaskInfo(
                 packageName = NOTE_TASK_PACKAGE_NAME,
                 uid = NOTE_TASK_UID,
+                user = UserHandle.of(0),
             )
         private val NOTE_RUNNING_TASK_INFO =
             ActivityManager.RunningTaskInfo().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
index a4df346..b4f5528 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.notetask
 
+import android.os.UserHandle
 import android.test.suitebuilder.annotation.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.internal.logging.UiEventLogger
@@ -44,7 +45,7 @@
         NoteTaskEventLogger(uiEventLogger)
 
     private fun createNoteTaskInfo(): NoteTaskInfo =
-        NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
+        NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0))
 
     @Before
     fun setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
index 0c945df..e09c804 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
@@ -22,8 +22,6 @@
 import android.test.suitebuilder.annotation.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -46,14 +44,13 @@
 
     @Mock lateinit var packageManager: PackageManager
     @Mock lateinit var roleManager: RoleManager
-    private val userTracker: UserTracker = FakeUserTracker()
 
     private lateinit var underTest: NoteTaskInfoResolver
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        underTest = NoteTaskInfoResolver(roleManager, packageManager, userTracker)
+        underTest = NoteTaskInfoResolver(roleManager, packageManager)
     }
 
     @Test
@@ -72,11 +69,12 @@
             )
             .thenReturn(ApplicationInfo().apply { this.uid = uid })
 
-        val actual = underTest.resolveInfo()
+        val actual = underTest.resolveInfo(user = context.user)
 
         requireNotNull(actual) { "Note task info must not be null" }
         assertThat(actual.packageName).isEqualTo(packageName)
         assertThat(actual.uid).isEqualTo(uid)
+        assertThat(actual.user).isEqualTo(context.user)
     }
 
     @Test
@@ -94,11 +92,12 @@
             )
             .thenThrow(PackageManager.NameNotFoundException(packageName))
 
-        val actual = underTest.resolveInfo()
+        val actual = underTest.resolveInfo(user = context.user)
 
         requireNotNull(actual) { "Note task info must not be null" }
         assertThat(actual.packageName).isEqualTo(packageName)
         assertThat(actual.uid).isEqualTo(0)
+        assertThat(actual.user).isEqualTo(context.user)
     }
 
     @Test
@@ -107,7 +106,7 @@
             emptyList<String>()
         }
 
-        val actual = underTest.resolveInfo()
+        val actual = underTest.resolveInfo(user = context.user)
 
         assertThat(actual).isNull()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 91cd6ae..3435450 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.notetask
 
+import android.os.UserHandle
 import android.test.suitebuilder.annotation.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
@@ -28,7 +29,7 @@
 internal class NoteTaskInfoTest : SysuiTestCase() {
 
     private fun createNoteTaskInfo(): NoteTaskInfo =
-        NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
+        NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0))
 
     @Test
     fun launchMode_keyguardLocked_launchModeActivity() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
index 249a91b..bb3b3f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -66,7 +66,7 @@
     }
 
     @Test
-    fun `isInteractive - registers for broadcasts`() =
+    fun isInteractive_registersForBroadcasts() =
         runBlocking(IMMEDIATE) {
             val job = underTest.isInteractive.onEach {}.launchIn(this)
 
@@ -78,7 +78,7 @@
         }
 
     @Test
-    fun `isInteractive - unregisters from broadcasts`() =
+    fun isInteractive_unregistersFromBroadcasts() =
         runBlocking(IMMEDIATE) {
             val job = underTest.isInteractive.onEach {}.launchIn(this)
             verifyRegistered()
@@ -89,7 +89,7 @@
         }
 
     @Test
-    fun `isInteractive - emits initial true value if screen was on`() =
+    fun isInteractive_emitsInitialTrueValueIfScreenWasOn() =
         runBlocking(IMMEDIATE) {
             isInteractive = true
             var value: Boolean? = null
@@ -102,7 +102,7 @@
         }
 
     @Test
-    fun `isInteractive - emits initial false value if screen was off`() =
+    fun isInteractive_emitsInitialFalseValueIfScreenWasOff() =
         runBlocking(IMMEDIATE) {
             isInteractive = false
             var value: Boolean? = null
@@ -115,7 +115,7 @@
         }
 
     @Test
-    fun `isInteractive - emits true when the screen turns on`() =
+    fun isInteractive_emitsTrueWhenTheScreenTurnsOn() =
         runBlocking(IMMEDIATE) {
             var value: Boolean? = null
             val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
@@ -129,7 +129,7 @@
         }
 
     @Test
-    fun `isInteractive - emits false when the screen turns off`() =
+    fun isInteractive_emitsFalseWhenTheScreenTurnsOff() =
         runBlocking(IMMEDIATE) {
             var value: Boolean? = null
             val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
@@ -143,7 +143,7 @@
         }
 
     @Test
-    fun `isInteractive - emits correctly over time`() =
+    fun isInteractive_emitsCorrectlyOverTime() =
         runBlocking(IMMEDIATE) {
             val values = mutableListOf<Boolean>()
             val job = underTest.isInteractive.onEach(values::add).launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
index bf6a37e..31d4512 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -47,7 +47,7 @@
     }
 
     @Test
-    fun `isInteractive - screen turns off`() =
+    fun isInteractive_screenTurnsOff() =
         runBlocking(IMMEDIATE) {
             repository.setInteractive(true)
             var value: Boolean? = null
@@ -60,7 +60,7 @@
         }
 
     @Test
-    fun `isInteractive - becomes interactive`() =
+    fun isInteractive_becomesInteractive() =
         runBlocking(IMMEDIATE) {
             repository.setInteractive(false)
             var value: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 8cb5d31..355c4b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -153,7 +153,7 @@
     }
 
     @Test
-    fun `WakefulnessLifecycle - dispatchFinishedWakingUp sets SysUI flag to AWAKE`() {
+    fun wakefulnessLifecycle_dispatchFinishedWakingUpSetsSysUIflagToAWAKE() {
         // WakefulnessLifecycle is initialized to AWAKE initially, and won't emit a noop.
         wakefulnessLifecycle.dispatchFinishedGoingToSleep()
         clearInvocations(overviewProxy)
@@ -167,7 +167,7 @@
     }
 
     @Test
-    fun `WakefulnessLifecycle - dispatchStartedWakingUp sets SysUI flag to WAKING`() {
+    fun wakefulnessLifecycle_dispatchStartedWakingUpSetsSysUIflagToWAKING() {
         wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN)
 
         verify(overviewProxy)
@@ -177,7 +177,7 @@
     }
 
     @Test
-    fun `WakefulnessLifecycle - dispatchFinishedGoingToSleep sets SysUI flag to ASLEEP`() {
+    fun wakefulnessLifecycle_dispatchFinishedGoingToSleepSetsSysUIflagToASLEEP() {
         wakefulnessLifecycle.dispatchFinishedGoingToSleep()
 
         verify(overviewProxy)
@@ -187,7 +187,7 @@
     }
 
     @Test
-    fun `WakefulnessLifecycle - dispatchStartedGoingToSleep sets SysUI flag to GOING_TO_SLEEP`() {
+    fun wakefulnessLifecycle_dispatchStartedGoingToSleepSetsSysUIflagToGOING_TO_SLEEP() {
         wakefulnessLifecycle.dispatchStartedGoingToSleep(
             PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 57b6b2b..beb981d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -73,7 +73,7 @@
     }
 
     @Test
-    fun `calls callback and updates profiles when an intent received`() {
+    fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() {
         tracker.initialize(0)
         tracker.addCallback(callback, executor)
         val profileID = tracker.userId + 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index 76aa08a..d7c06a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.shade
 
 import android.hardware.display.AmbientDisplayConfiguration
+import android.os.PowerManager
 import android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE
 import android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
-import android.os.PowerManager
 import android.view.MotionEvent
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -38,15 +38,14 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper(setAsMainLooper = true)
@@ -112,7 +111,7 @@
 
         // THEN wake up device if dozing
         verify(centralSurfaces).wakeUpIfDozing(
-                anyLong(), any(), anyString(), eq(PowerManager.WAKE_REASON_TAP))
+                anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP))
     }
 
     @Test
@@ -132,7 +131,7 @@
 
         // THEN wake up device if dozing
         verify(centralSurfaces).wakeUpIfDozing(
-                anyLong(), any(), anyString(), eq(PowerManager.WAKE_REASON_TAP))
+                anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP))
     }
 
     @Test
@@ -164,7 +163,7 @@
 
         // THEN the device doesn't wake up
         verify(centralSurfaces, never()).wakeUpIfDozing(
-                anyLong(), any(), anyString(), anyInt())
+                anyLong(), anyString(), anyInt())
     }
 
     @Test
@@ -212,7 +211,7 @@
 
         // THEN the device doesn't wake up
         verify(centralSurfaces, never()).wakeUpIfDozing(
-                anyLong(), any(), anyString(), anyInt())
+                anyLong(), anyString(), anyInt())
     }
 
     @Test
@@ -232,7 +231,7 @@
 
         // THEN the device doesn't wake up
         verify(centralSurfaces, never()).wakeUpIfDozing(
-                anyLong(), any(), anyString(), anyInt())
+                anyLong(), anyString(), anyInt())
     }
 
     @Test
@@ -252,7 +251,7 @@
 
         // THEN the device doesn't wake up
         verify(centralSurfaces, never()).wakeUpIfDozing(
-                anyLong(), any(), anyString(), anyInt())
+                anyLong(), anyString(), anyInt())
     }
 
     fun updateSettings() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index b547318..d421aca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -54,7 +54,7 @@
     }
 
     @Test
-    fun `returns MODE_ON for qqs with center cutout`() {
+    fun returnsMODE_ONforQqsWithCenterCutout() {
         assertThat(
                 controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.prevFrameToFraction())
             )
@@ -62,13 +62,13 @@
     }
 
     @Test
-    fun `returns MODE_ESTIMATE for qs with center cutout`() {
+    fun returnsMODE_ESTIMATEforQsWithCenterCutout() {
         assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
             .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
     }
 
     @Test
-    fun `returns MODE_ON for qqs with corner cutout`() {
+    fun returnsMODE_ONforQqsWithCornerCutout() {
         whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
 
         assertThat(
@@ -78,7 +78,7 @@
     }
 
     @Test
-    fun `returns MODE_ESTIMATE for qs with corner cutout`() {
+    fun returnsMODE_ESTIMATEforQsWithCornerCutout() {
         whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
 
         assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
@@ -86,7 +86,7 @@
     }
 
     @Test
-    fun `returns null in-between`() {
+    fun returnsNullInBetween() {
         assertThat(
                 controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.nextFrameToFraction())
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 76f7401..9fe75ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -366,7 +366,7 @@
     }
 
     @Test
-    fun `battery mode controller called when qsExpandedFraction changes`() {
+    fun batteryModeControllerCalledWhenQsExpandedFractionChanges() {
         whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(0f)))
             .thenReturn(BatteryMeterView.MODE_ON)
         whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(1f)))
@@ -825,7 +825,7 @@
     }
 
     @Test
-    fun `carrier left padding is set when clock layout changes`() {
+    fun carrierLeftPaddingIsSetWhenClockLayoutChanges() {
         val width = 200
         whenever(clock.width).thenReturn(width)
         whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 7fa27f3..3f3faf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -201,7 +201,7 @@
     @Test
     fun test_aodClock_always_whiteColor() {
         val clock = provider.createClock(DEFAULT_CLOCK_ID)
-        clock.animations.doze(0.9f) // set AOD mode to active
+        clock.smallClock.animations.doze(0.9f) // set AOD mode to active
         clock.smallClock.events.onRegionDarknessChanged(true)
         verify((clock.smallClock.view as AnimatableClockView), never()).animateAppearOnLockscreen()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
new file mode 100644
index 0000000..2b4a7fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -0,0 +1,135 @@
+package com.android.systemui.shared.condition
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConditionExtensionsTest : SysuiTestCase() {
+    private lateinit var testScope: TestScope
+
+    @Before
+    fun setUp() {
+        testScope = TestScope(StandardTestDispatcher())
+    }
+
+    @Test
+    fun flowInitiallyTrue() =
+        testScope.runTest {
+            val flow = flowOf(true)
+            val condition = flow.toCondition(this)
+
+            runCurrent()
+            assertThat(condition.isConditionSet).isFalse()
+
+            condition.start()
+            runCurrent()
+            assertThat(condition.isConditionSet).isTrue()
+            assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun flowInitiallyFalse() =
+        testScope.runTest {
+            val flow = flowOf(false)
+            val condition = flow.toCondition(this)
+
+            runCurrent()
+            assertThat(condition.isConditionSet).isFalse()
+
+            condition.start()
+            runCurrent()
+            assertThat(condition.isConditionSet).isTrue()
+            assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun emptyFlowWithNoInitialValue() =
+        testScope.runTest {
+            val flow = emptyFlow<Boolean>()
+            val condition = flow.toCondition(this)
+            condition.start()
+
+            runCurrent()
+            assertThat(condition.isConditionSet).isFalse()
+            assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun emptyFlowWithInitialValueOfTrue() =
+        testScope.runTest {
+            val flow = emptyFlow<Boolean>()
+            val condition = flow.toCondition(scope = this, initialValue = true)
+            condition.start()
+
+            runCurrent()
+            assertThat(condition.isConditionSet).isTrue()
+            assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun emptyFlowWithInitialValueOfFalse() =
+        testScope.runTest {
+            val flow = emptyFlow<Boolean>()
+            val condition = flow.toCondition(scope = this, initialValue = false)
+            condition.start()
+
+            runCurrent()
+            assertThat(condition.isConditionSet).isTrue()
+            assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun conditionUpdatesWhenFlowEmitsNewValue() =
+        testScope.runTest {
+            val flow = MutableStateFlow(false)
+            val condition = flow.toCondition(this)
+            condition.start()
+
+            runCurrent()
+            assertThat(condition.isConditionSet).isTrue()
+            assertThat(condition.isConditionMet).isFalse()
+
+            flow.value = true
+            runCurrent()
+            assertThat(condition.isConditionMet).isTrue()
+
+            flow.value = false
+            runCurrent()
+            assertThat(condition.isConditionMet).isFalse()
+
+            condition.stop()
+        }
+
+    @Test
+    fun stoppingConditionUnsubscribesFromFlow() =
+        testScope.runTest {
+            val flow = MutableSharedFlow<Boolean>()
+            val condition = flow.toCondition(this)
+            runCurrent()
+            assertThat(flow.subscriptionCount.value).isEqualTo(0)
+
+            condition.start()
+            runCurrent()
+            assertThat(flow.subscriptionCount.value).isEqualTo(1)
+
+            condition.stop()
+            runCurrent()
+            assertThat(flow.subscriptionCount.value).isEqualTo(0)
+        }
+}
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 932a1f9..d017ffd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -13,7 +13,7 @@
 import com.android.systemui.media.controls.ui.MediaHierarchyManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
-import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -44,8 +44,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private fun <T> anyObject(): T {
     return Mockito.anyObject<T>()
@@ -69,7 +69,7 @@
     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
     @Mock lateinit var scrimController: ScrimController
     @Mock lateinit var falsingManager: FalsingManager
-    @Mock lateinit var notificationPanelController: NotificationPanelViewController
+    @Mock lateinit var shadeViewController: ShadeViewController
     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
     @Mock lateinit var depthController: NotificationShadeDepthController
     @Mock lateinit var stackscroller: NotificationStackScrollLayout
@@ -128,7 +128,7 @@
         transitionController.addCallback(transitionControllerCallback)
         whenever(nsslController.view).thenReturn(stackscroller)
         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
-        transitionController.notificationPanelController = notificationPanelController
+        transitionController.shadeViewController = shadeViewController
         transitionController.centralSurfaces = mCentralSurfaces
         transitionController.qS = qS
         transitionController.setStackScroller(nsslController)
@@ -223,7 +223,7 @@
     fun testGoToLockedShadeCreatesQSAnimation() {
         transitionController.goToLockedShade(null)
         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
-        verify(notificationPanelController).transitionToExpandedShade(anyLong())
+        verify(shadeViewController).transitionToExpandedShade(anyLong())
         assertNotNull(transitionController.dragDownAnimator)
     }
 
@@ -231,7 +231,7 @@
     fun testGoToLockedShadeDoesntCreateQSAnimation() {
         transitionController.goToLockedShade(null, needsQSAnimation = false)
         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
-        verify(notificationPanelController).transitionToExpandedShade(anyLong())
+        verify(shadeViewController).transitionToExpandedShade(anyLong())
         assertNull(transitionController.dragDownAnimator)
     }
 
@@ -239,7 +239,7 @@
     fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() {
         enableSplitShade()
         transitionController.goToLockedShade(null, needsQSAnimation = true)
-        verify(notificationPanelController).transitionToExpandedShade(anyLong())
+        verify(shadeViewController).transitionToExpandedShade(anyLong())
         assertNotNull(transitionController.dragDownAnimator)
     }
 
@@ -293,7 +293,7 @@
     fun setDragAmount_setsKeyguardTransitionProgress() {
         transitionController.dragDownAmount = 10f
 
-        verify(notificationPanelController).setKeyguardTransitionProgress(anyFloat(), anyInt())
+        verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), anyInt())
     }
 
     @Test
@@ -303,7 +303,7 @@
         transitionController.dragDownAmount = 10f
 
         val expectedAlpha = 1 - 10f / alphaDistance
-        verify(notificationPanelController)
+        verify(shadeViewController)
                 .setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
     }
 
@@ -317,7 +317,7 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(notificationPanelController).setKeyguardTransitionProgress(anyFloat(), eq(0))
+        verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(0))
     }
 
     @Test
@@ -330,7 +330,7 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(notificationPanelController)
+        verify(shadeViewController)
             .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
     }
 
@@ -349,7 +349,7 @@
             context.resources.getDimensionPixelSize(
                 R.dimen.lockscreen_shade_keyguard_transition_vertical_offset)
         val expectedTranslation = 10f / distance * offset
-        verify(notificationPanelController)
+        verify(shadeViewController)
             .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
     }
 
@@ -468,7 +468,7 @@
         transitionController.dragDownAmount = dragDownAmount
 
         val expectedAlpha = 1 - dragDownAmount / alphaDistance
-        verify(notificationPanelController).setKeyguardStatusBarAlpha(expectedAlpha)
+        verify(shadeViewController).setKeyguardStatusBarAlpha(expectedAlpha)
     }
 
     @Test
@@ -477,7 +477,7 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(notificationPanelController).setKeyguardStatusBarAlpha(-1f)
+        verify(shadeViewController).setKeyguardStatusBarAlpha(-1f)
     }
 
     private fun enableSplitShade() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
new file mode 100644
index 0000000..b997f64
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.statusbar
+
+import android.app.Notification
+import android.app.WallpaperManager
+import android.graphics.Bitmap
+import android.media.MediaMetadata
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.colorextraction.SysuiColorExtractor
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.doCallRealMethod
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class NotificationMediaManagerTest : SysuiTestCase() {
+
+    @Mock private lateinit var centralSurfaces: CentralSurfaces
+    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var visibilityProvider: NotificationVisibilityProvider
+    @Mock private lateinit var mediaArtworkProcessor: MediaArtworkProcessor
+    @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+    @Mock private lateinit var notifPipeline: NotifPipeline
+    @Mock private lateinit var notifCollection: NotifCollection
+    @Mock private lateinit var mediaDataManager: MediaDataManager
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var colorExtractor: SysuiColorExtractor
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var dumpManager: DumpManager
+    @Mock private lateinit var wallpaperManager: WallpaperManager
+
+    @Mock private lateinit var notificationEntry: NotificationEntry
+
+    lateinit var manager: NotificationMediaManager
+    val clock = FakeSystemClock()
+    val mainExecutor: DelayableExecutor = FakeExecutor(clock)
+
+    @Mock private lateinit var mockManager: NotificationMediaManager
+    @Mock private lateinit var mockBackDropView: BackDropView
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        doCallRealMethod().whenever(mockManager).updateMediaMetaData(anyBoolean(), anyBoolean())
+        doReturn(mockBackDropView).whenever(mockManager).backDropView
+
+        manager =
+            NotificationMediaManager(
+                context,
+                Lazy { Optional.of(centralSurfaces) },
+                Lazy { notificationShadeWindowController },
+                visibilityProvider,
+                mediaArtworkProcessor,
+                keyguardBypassController,
+                notifPipeline,
+                notifCollection,
+                mainExecutor,
+                mediaDataManager,
+                statusBarStateController,
+                colorExtractor,
+                keyguardStateController,
+                dumpManager,
+                wallpaperManager,
+            )
+    }
+
+    /**
+     * Check that updateMediaMetaData is a no-op with mIsLockscreenLiveWallpaperEnabled = true
+     * Temporary test for the lock screen live wallpaper project.
+     *
+     * TODO(b/273443374): remove this test
+     */
+    @Test
+    fun testUpdateMediaMetaDataDisabled() {
+        mockManager.mIsLockscreenLiveWallpaperEnabled = true
+        for (metaDataChanged in listOf(true, false)) {
+            for (allowEnterAnimation in listOf(true, false)) {
+                mockManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
+                verify(mockManager, never()).mediaMetadata
+            }
+        }
+    }
+
+    @Test
+    fun testMetadataUpdated_doesNotRetainArtwork() {
+        val artBmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
+        val artUri = "content://example"
+        val inputMetadata =
+            MediaMetadata.Builder()
+                .putBitmap(MediaMetadata.METADATA_KEY_ART, artBmp)
+                .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, artBmp)
+                .putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, artBmp)
+                .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, artUri)
+                .putString(MediaMetadata.METADATA_KEY_ART_URI, artUri)
+                .putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, artUri)
+                .build()
+
+        // Create a playing media notification
+        val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
+        val session = MediaSession(context, "NotificationMediaManagerTest")
+        session.setMetadata(inputMetadata)
+        session.setPlaybackState(state)
+        val sbn =
+            SbnBuilder().run {
+                modifyNotification(context).also {
+                    it.setSmallIcon(android.R.drawable.ic_media_play)
+                    it.setStyle(
+                        Notification.MediaStyle().apply { setMediaSession(session.sessionToken) }
+                    )
+                }
+                build()
+            }
+        whenever(notificationEntry.sbn).thenReturn(sbn)
+        val collection = ArrayList<NotificationEntry>()
+        collection.add(notificationEntry)
+        whenever(notifPipeline.allNotifs).thenReturn(collection)
+
+        // Trigger update in NotificationMediaManager
+        manager.findAndUpdateMediaNotifications()
+
+        // Verify that there is no artwork data retained
+        val metadata = manager.mediaMetadata
+        assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)).isNull()
+        assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)).isNull()
+        assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON)).isNull()
+        assertThat(metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI)).isNull()
+        assertThat(metadata.getString(MediaMetadata.METADATA_KEY_ART_URI)).isNull()
+        assertThat(metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index aff705f..da3a9f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.NotificationPanelViewController.WAKEUP_ANIMATION_DELAY_MS
+import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_WAKEUP
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index ba91d87..67128ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
@@ -171,9 +170,6 @@
 
         // Set the default FSI decision
         setShouldFullScreen(any(), FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
-
-        // Run tests with default feature flag state
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(Flags.FSI_ON_DND_UPDATE.default)
     }
 
     @Test
@@ -258,7 +254,7 @@
 
     @Test
     fun testOnEntryAdded_shouldFullScreen() {
-        setShouldFullScreen(entry, FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN)
+        setShouldFullScreen(entry, FullScreenIntentDecision.FSI_KEYGUARD_SHOWING)
         collectionListener.onEntryAdded(entry)
         verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
     }
@@ -854,38 +850,7 @@
     }
 
     @Test
-    fun testOnRankingApplied_noFSIOnUpdateWhenFlagOff() {
-        // Ensure the feature flag is off
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(false)
-
-        // GIVEN that mEntry was previously suppressed from full-screen only by DND
-        setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
-        collectionListener.onEntryAdded(entry)
-
-        // Verify that this causes a log
-        verifyLoggedFullScreenIntentDecision(
-            entry,
-            FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
-        )
-        clearInterruptionProviderInvocations()
-
-        // and it is then updated to allow full screen
-        setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
-        whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
-        collectionListener.onRankingApplied()
-
-        // THEN it should not full screen because the feature is off
-        verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
-
-        // VERIFY that no additional logging happens either
-        verifyNoFullScreenIntentDecisionLogged()
-    }
-
-    @Test
     fun testOnRankingApplied_updateToFullScreen() {
-        // Turn on the feature
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
-
         // GIVEN that mEntry was previously suppressed from full-screen only by DND
         setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
         collectionListener.onEntryAdded(entry)
@@ -929,9 +894,6 @@
 
     @Test
     fun testOnRankingApplied_withOnlyDndSuppressionAllowsFsiLater() {
-        // Turn on the feature
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
-
         // GIVEN that mEntry was previously suppressed from full-screen only by DND
         setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
         collectionListener.onEntryAdded(entry)
@@ -980,9 +942,6 @@
 
     @Test
     fun testOnRankingApplied_newNonFullScreenAnswerInvalidatesCandidate() {
-        // Turn on the feature
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
-
         // GIVEN that mEntry was previously suppressed from full-screen only by DND
         whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
         setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
@@ -1018,9 +977,6 @@
 
     @Test
     fun testOnRankingApplied_noFSIWhenAlsoSuppressedForOtherReasons() {
-        // Feature on
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
-
         // GIVEN that mEntry is suppressed by DND (functionally), but not *only* DND
         setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND)
         collectionListener.onEntryAdded(entry)
@@ -1035,9 +991,6 @@
 
     @Test
     fun testOnRankingApplied_noFSIWhenTooOld() {
-        // Feature on
-        whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
-
         // GIVEN that mEntry is suppressed only by DND
         setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
         collectionListener.onEntryAdded(entry)
@@ -1046,7 +999,7 @@
         coordinator.addForFSIReconsideration(entry, systemClock.currentTimeMillis() - 10000)
 
         // and it is updated to full screen later
-        setShouldFullScreen(entry, FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN)
+        setShouldFullScreen(entry, FullScreenIntentDecision.FSI_KEYGUARD_SHOWING)
         collectionListener.onRankingApplied()
 
         // THEN it should still not full screen because it's too old
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index c2a2a40..8f07f8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -97,8 +97,6 @@
 
     @Test
     fun unseenFilterSuppressesSeenNotifWhileKeyguardShowing() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(true)
@@ -124,8 +122,6 @@
 
     @Test
     fun unseenFilterStopsMarkingSeenNotifWhenTransitionToAod() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is not expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(false)
@@ -151,8 +147,6 @@
 
     @Test
     fun unseenFilter_headsUpMarkedAsSeen() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is not expanded
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(false)
@@ -183,14 +177,12 @@
 
     @Test
     fun unseenFilterDoesNotSuppressSeenOngoingNotifWhileKeyguardShowing() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is expanded, and an ongoing notification is present
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             val fakeEntry = NotificationEntryBuilder()
-                .setNotification(Notification.Builder(mContext).setOngoing(true).build())
+                    .setNotification(Notification.Builder(mContext, "id").setOngoing(true).build())
                 .build()
             collectionListener.onEntryAdded(fakeEntry)
 
@@ -205,8 +197,6 @@
 
     @Test
     fun unseenFilterDoesNotSuppressSeenMediaNotifWhileKeyguardShowing() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is expanded, and a media notification is present
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(true)
@@ -229,8 +219,6 @@
 
     @Test
     fun unseenFilterUpdatesSeenProviderWhenSuppressing() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(true)
@@ -255,8 +243,6 @@
 
     @Test
     fun unseenFilterInvalidatesWhenSettingChanges() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, and shade is expanded
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(true)
@@ -292,8 +278,6 @@
 
     @Test
     fun unseenFilterAllowsNewNotif() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is showing, no notifications present
         keyguardRepository.setKeyguardShowing(true)
         runKeyguardCoordinatorTest {
@@ -308,8 +292,6 @@
 
     @Test
     fun unseenFilterSeenGroupSummaryWithUnseenChild() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
         whenever(statusBarStateController.isExpanded).thenReturn(true)
@@ -342,8 +324,6 @@
 
     @Test
     fun unseenNotificationIsMarkedAsSeenWhenKeyguardGoesAway() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is showing, not dozing, unseen notification is present
         keyguardRepository.setKeyguardShowing(true)
         keyguardRepository.setDozing(false)
@@ -370,8 +350,6 @@
 
     @Test
     fun unseenNotificationIsNotMarkedAsSeenIfShadeNotExpanded() {
-        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
-
         // GIVEN: Keyguard is showing, unseen notification is present
         keyguardRepository.setKeyguardShowing(true)
         runKeyguardCoordinatorTest {
@@ -441,20 +419,15 @@
         val unseenFilter: NotifFilter
             get() = keyguardCoordinator.unseenNotifFilter
 
-        // TODO(254647461): Remove lazy from these properties once
-        //    Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD is enabled and removed
-
-        val collectionListener: NotifCollectionListener by lazy {
-            withArgCaptor { verify(notifPipeline).addCollectionListener(capture()) }
+        val collectionListener: NotifCollectionListener = withArgCaptor {
+            verify(notifPipeline).addCollectionListener(capture())
         }
 
-        val onHeadsUpChangedListener: OnHeadsUpChangedListener by lazy {
+        val onHeadsUpChangedListener: OnHeadsUpChangedListener get() =
             withArgCaptor { verify(headsUpManager).addListener(capture()) }
-        }
 
-        val statusBarStateListener: StatusBarStateController.StateListener by lazy {
+        val statusBarStateListener: StatusBarStateController.StateListener get() =
             withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
-        }
 
         var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
             get() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index ae6ced4..d3e5816 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -123,7 +123,6 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
 
         mUiEventLoggerFake = new UiEventLoggerFake();
@@ -544,12 +543,6 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldNotFullScreen_notPendingIntent();
-    }
-
-    @Test
     public void testShouldNotFullScreen_notPendingIntent() throws RemoteException {
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -604,12 +597,6 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_notHighImportance_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldNotFullScreen_notHighImportance();
-    }
-
-    @Test
     public void testShouldNotFullScreen_notHighImportance() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -626,12 +613,6 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_isGroupAlertSilenced_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldNotFullScreen_isGroupAlertSilenced();
-    }
-
-    @Test
     public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true);
         when(mPowerManager.isInteractive()).thenReturn(false);
@@ -656,12 +637,6 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_isSuppressedByBubbleMetadata_withStrictFlag() {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldNotFullScreen_isSuppressedByBubbleMetadata();
-    }
-
-    @Test
     public void testShouldNotFullScreen_isSuppressedByBubbleMetadata() {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo")
@@ -689,12 +664,6 @@
     }
 
     @Test
-    public void testShouldFullScreen_notInteractive_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldFullScreen_notInteractive();
-    }
-
-    @Test
     public void testShouldFullScreen_notInteractive() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo")
@@ -714,12 +683,6 @@
     }
 
     @Test
-    public void testShouldFullScreen_isDreaming_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldFullScreen_isDreaming();
-    }
-
-    @Test
     public void testShouldFullScreen_isDreaming() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -736,12 +699,6 @@
     }
 
     @Test
-    public void testShouldFullScreen_onKeyguard_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldFullScreen_onKeyguard();
-    }
-
-    @Test
     public void testShouldFullScreen_onKeyguard() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -758,12 +715,6 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_willHun_withStrictFlag() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
-        testShouldNotFullScreen_willHun();
-    }
-
-    @Test
     public void testShouldNotFullScreen_willHun() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -781,26 +732,7 @@
     }
 
     @Test
-    public void testShouldFullScreen_packageSnoozed() throws RemoteException {
-        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
-        when(mPowerManager.isInteractive()).thenReturn(true);
-        when(mPowerManager.isScreenOn()).thenReturn(true);
-        when(mStatusBarStateController.isDreaming()).thenReturn(false);
-        when(mStatusBarStateController.getState()).thenReturn(SHADE);
-        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
-
-        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN);
-        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
-                .isTrue();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
-        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
-        verify(mLogger).logFullscreen(entry, "FSI_EXPECTED_NOT_TO_HUN");
-    }
-
-    @Test
-    public void testShouldNotFullScreen_snoozed_occluding_withStrictRules() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testShouldNotFullScreen_snoozed_occluding() throws Exception {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
         when(mPowerManager.isScreenOn()).thenReturn(true);
@@ -820,8 +752,7 @@
     }
 
     @Test
-    public void testShouldHeadsUp_snoozed_occluding_withStrictRules() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testShouldHeadsUp_snoozed_occluding() throws Exception {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
         when(mPowerManager.isScreenOn()).thenReturn(true);
@@ -845,8 +776,7 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testShouldNotFullScreen_snoozed_lockedShade() throws Exception {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
         when(mPowerManager.isScreenOn()).thenReturn(true);
@@ -866,8 +796,7 @@
     }
 
     @Test
-    public void testShouldHeadsUp_snoozed_lockedShade_withStrictRules() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testShouldHeadsUp_snoozed_lockedShade() throws Exception {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
         when(mPowerManager.isScreenOn()).thenReturn(true);
@@ -891,8 +820,7 @@
     }
 
     @Test
-    public void testShouldNotFullScreen_snoozed_unlocked_withStrictRules() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testShouldNotFullScreen_snoozed_unlocked() throws Exception {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
         when(mPowerManager.isScreenOn()).thenReturn(true);
@@ -955,8 +883,7 @@
     }
 
     @Test
-    public void testShouldHeadsUp_snoozed_unlocked_withStrictRules() throws Exception {
-        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testShouldHeadsUp_snoozed_unlocked() throws Exception {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
         when(mPowerManager.isScreenOn()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 60bc3a4..c2f1f61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -145,8 +144,6 @@
 
     @Test
     fun offerKeepInParent_parentDismissed() {
-        whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
-            .thenReturn(true)
         whenever(view.isParentDismissed).thenReturn(true)
 
         Assert.assertTrue(controller.offerToKeepInParentForAnimation())
@@ -155,9 +152,6 @@
 
     @Test
     fun offerKeepInParent_parentNotDismissed() {
-        whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
-            .thenReturn(true)
-
         Assert.assertFalse(controller.offerToKeepInParentForAnimation())
         Mockito.verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
new file mode 100644
index 0000000..c960230
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ActivatableNotificationViewModelTest : SysuiTestCase() {
+
+    // fakes
+    private val a11yRepo = FakeAccessibilityRepository()
+
+    // real impls
+    private val a11yInteractor = AccessibilityInteractor(a11yRepo)
+    private val underTest = ActivatableNotificationViewModel(a11yInteractor)
+
+    @Test
+    fun isTouchable_whenA11yTouchExplorationDisabled() = runTest {
+        a11yRepo.isTouchExplorationEnabled.value = false
+        val isTouchable: Boolean? by collectLastValue(underTest.isTouchable)
+        assertThat(isTouchable).isTrue()
+    }
+
+    @Test
+    fun isNotTouchable_whenA11yTouchExplorationEnabled() = runTest {
+        a11yRepo.isTouchExplorationEnabled.value = true
+        val isTouchable: Boolean? by collectLastValue(underTest.isTouchable)
+        assertThat(isTouchable).isFalse()
+    }
+
+    @Test
+    fun isTouchable_whenA11yTouchExplorationChangesToDisabled() = runTest {
+        a11yRepo.isTouchExplorationEnabled.value = true
+        val isTouchable: Boolean? by collectLastValue(underTest.isTouchable)
+        runCurrent()
+        a11yRepo.isTouchExplorationEnabled.value = false
+        assertThat(isTouchable).isTrue()
+    }
+
+    @Test
+    fun isNotTouchable_whenA11yTouchExplorationChangesToEnabled() = runTest {
+        a11yRepo.isTouchExplorationEnabled.value = false
+        val isTouchable: Boolean? by collectLastValue(underTest.isTouchable)
+        runCurrent()
+        a11yRepo.isTouchExplorationEnabled.value = true
+        assertThat(isTouchable).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 2cc375b..944eb2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -18,17 +18,27 @@
 
 package com.android.systemui.statusbar.notification.shelf.domain.interactor
 
+import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.verify
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -36,8 +46,17 @@
 
     private val keyguardRepository = FakeKeyguardRepository()
     private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository()
+    private val centralSurfaces: CentralSurfaces = mock()
+    private val systemClock = FakeSystemClock()
+    private val keyguardTransitionController: LockscreenShadeTransitionController = mock()
     private val underTest =
-        NotificationShelfInteractor(keyguardRepository, deviceEntryFaceAuthRepository)
+        NotificationShelfInteractor(
+            keyguardRepository,
+            deviceEntryFaceAuthRepository,
+            centralSurfaces,
+            systemClock,
+            keyguardTransitionController,
+        )
 
     @Test
     fun shelfIsNotStatic_whenKeyguardNotShowing() = runTest {
@@ -85,4 +104,19 @@
 
         assertThat(onKeyguard).isFalse()
     }
+
+    @Test
+    fun goToLockedShadeFromShelf_wakesUpFromDoze() {
+        underTest.goToLockedShadeFromShelf()
+
+        verify(centralSurfaces)
+            .wakeUpIfDozing(anyLong(), any(), eq(PowerManager.WAKE_REASON_GESTURE))
+    }
+
+    @Test
+    fun goToLockedShadeFromShelf_invokesKeyguardTransitionController() {
+        underTest.goToLockedShadeFromShelf()
+
+        verify(keyguardTransitionController).goToLockedShade(isNull(), eq(true))
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 439edaf..e9a8f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -18,28 +18,64 @@
 
 package com.android.systemui.statusbar.notification.shelf.ui.viewmodel
 
+import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class NotificationShelfViewModelTest : SysuiTestCase() {
 
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    // mocks
+    @Mock private lateinit var centralSurfaces: CentralSurfaces
+    @Mock private lateinit var keyguardTransitionController: LockscreenShadeTransitionController
+
+    // fakes
     private val keyguardRepository = FakeKeyguardRepository()
     private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository()
-    private val interactor =
-        NotificationShelfInteractor(keyguardRepository, deviceEntryFaceAuthRepository)
-    private val underTest = NotificationShelfViewModel(interactor)
+    private val systemClock = FakeSystemClock()
+    private val a11yRepo = FakeAccessibilityRepository()
+
+    // real impls
+    private val a11yInteractor = AccessibilityInteractor(a11yRepo)
+    private val activatableViewModel = ActivatableNotificationViewModel(a11yInteractor)
+    private val interactor by lazy {
+        NotificationShelfInteractor(
+            keyguardRepository,
+            deviceEntryFaceAuthRepository,
+            centralSurfaces,
+            systemClock,
+            keyguardTransitionController,
+        )
+    }
+    private val underTest by lazy { NotificationShelfViewModel(interactor, activatableViewModel) }
 
     @Test
     fun canModifyColorOfNotifications_whenKeyguardNotShowing() = runTest {
@@ -87,4 +123,13 @@
 
         assertThat(isClickable).isFalse()
     }
+
+    @Test
+    fun onClicked_goesToLockedShade() {
+        underTest.onShelfClicked()
+
+        verify(centralSurfaces)
+            .wakeUpIfDozing(ArgumentMatchers.anyLong(), any(), eq(PowerManager.WAKE_REASON_GESTURE))
+        verify(keyguardTransitionController).goToLockedShade(Mockito.isNull(), eq(true))
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 485f2be..45b1f4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -403,7 +403,6 @@
 
     @Test
     public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
-        when(mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()).thenReturn(true);
         mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true);
         mController.attach(mNotificationStackScrollLayout);
         mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 775d267..872c560 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -46,9 +46,9 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
@@ -76,7 +76,7 @@
     @Mock private ShadeController mShadeController;
     @Mock private CommandQueue mCommandQueue;
     @Mock private QuickSettingsController mQuickSettingsController;
-    @Mock private NotificationPanelViewController mNotificationPanelViewController;
+    @Mock private ShadeViewController mShadeViewController;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -110,7 +110,7 @@
                 mContext.getResources(),
                 mShadeController,
                 mCommandQueue,
-                mNotificationPanelViewController,
+                mShadeViewController,
                 mRemoteInputQuickSettingsDisabler,
                 mMetricsLogger,
                 mKeyguardUpdateMonitor,
@@ -153,9 +153,9 @@
 
         // Trying to open it does nothing.
         mSbcqCallbacks.animateExpandNotificationsPanel();
-        verify(mNotificationPanelViewController, never()).expandToNotifications();
+        verify(mShadeViewController, never()).expandToNotifications();
         mSbcqCallbacks.animateExpandSettingsPanel(null);
-        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
+        verify(mShadeViewController, never()).expand(anyBoolean());
     }
 
     @Test
@@ -171,9 +171,9 @@
 
         // Can now be opened.
         mSbcqCallbacks.animateExpandNotificationsPanel();
-        verify(mNotificationPanelViewController).expandToNotifications();
+        verify(mShadeViewController).expandToNotifications();
         mSbcqCallbacks.animateExpandSettingsPanel(null);
-        verify(mNotificationPanelViewController).expandToQs();
+        verify(mShadeViewController).expandToQs();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 6332069..ff3cea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -387,7 +387,6 @@
         when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
         when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
         when(mPowerManagerService.isInteractive()).thenReturn(true);
-        when(mStackScroller.getActivatedChild()).thenReturn(null);
 
         doAnswer(invocation -> {
             OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
@@ -562,7 +561,7 @@
         // TODO: we should be able to call mCentralSurfaces.start() and have all the below values
         // initialized automatically and make NPVC private.
         mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
-        mCentralSurfaces.mNotificationPanelViewController = mNotificationPanelViewController;
+        mCentralSurfaces.mShadeSurface = mNotificationPanelViewController;
         mCentralSurfaces.mQsController = mQuickSettingsController;
         mCentralSurfaces.mDozeScrimController = mDozeScrimController;
         mCentralSurfaces.mPresenter = mNotificationPresenter;
@@ -1206,34 +1205,6 @@
     }
 
     @Test
-    public void collapseShadeForBugReport_callsanimateCollapseShade_whenFlagDisabled() {
-        // GIVEN the shade is expanded & flag enabled
-        mCentralSurfaces.onShadeExpansionFullyChanged(true);
-        mCentralSurfaces.setBarStateForTest(SHADE);
-        mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, false);
-
-        // WHEN collapseShadeForBugreport is called
-        mCentralSurfaces.collapseShadeForBugreport();
-
-        // VERIFY that animateCollapseShade is called
-        verify(mShadeController).animateCollapseShade();
-    }
-
-    @Test
-    public void collapseShadeForBugReport_doesNotCallanimateCollapseShade_whenFlagEnabled() {
-        // GIVEN the shade is expanded & flag enabled
-        mCentralSurfaces.onShadeExpansionFullyChanged(true);
-        mCentralSurfaces.setBarStateForTest(SHADE);
-        mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, true);
-
-        // WHEN collapseShadeForBugreport is called
-        mCentralSurfaces.collapseShadeForBugreport();
-
-        // VERIFY that animateCollapseShade is called
-        verify(mShadeController, never()).animateCollapseShade();
-    }
-
-    @Test
     public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
         setFoldedStates(FOLD_STATE_FOLDED);
         setGoToSleepStates(FOLD_STATE_FOLDED);
@@ -1327,7 +1298,7 @@
 
         // WHEN wakeup is requested
         final int wakeReason = PowerManager.WAKE_REASON_TAP;
-        mCentralSurfaces.wakeUpIfDozing(0, null, "", wakeReason);
+        mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason);
 
         // THEN power manager receives wakeup
         verify(mPowerManagerService).wakeUp(eq(0L), eq(wakeReason), anyString(), anyString());
@@ -1341,7 +1312,7 @@
 
         // WHEN wakeup is requested
         final int wakeReason = PowerManager.WAKE_REASON_TAP;
-        mCentralSurfaces.wakeUpIfDozing(0, null, "", wakeReason);
+        mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason);
 
         // THEN power manager receives wakeup
         verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 996851e..2831d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -41,8 +41,8 @@
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
@@ -87,7 +87,7 @@
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    @Mock private NotificationPanelViewController mNotificationPanel;
+    @Mock private ShadeViewController mShadeViewController;
     @Mock private View mAmbientIndicationContainer;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private AuthController mAuthController;
@@ -108,7 +108,7 @@
                 mCentralSurfaces,
                 mStatusBarKeyguardViewManager,
                 mNotificationShadeWindowViewController,
-                mNotificationPanel,
+                mShadeViewController,
                 mAmbientIndicationContainer);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 3372dc3..205cebd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -38,8 +38,8 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeHeadsUpTracker;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -65,8 +65,8 @@
 
     private final NotificationStackScrollLayoutController mStackScrollerController =
             mock(NotificationStackScrollLayoutController.class);
-    private final NotificationPanelViewController mPanelView =
-            mock(NotificationPanelViewController.class);
+    private final ShadeViewController mShadeViewController =
+            mock(ShadeViewController.class);
     private final ShadeHeadsUpTracker mShadeHeadsUpTracker = mock(ShadeHeadsUpTracker.class);
     private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
@@ -104,7 +104,7 @@
         mCommandQueue = mock(CommandQueue.class);
         mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
         mFeatureFlag = mock(FeatureFlags.class);
-        when(mPanelView.getShadeHeadsUpTracker()).thenReturn(mShadeHeadsUpTracker);
+        when(mShadeViewController.getShadeHeadsUpTracker()).thenReturn(mShadeHeadsUpTracker);
         when(mFeatureFlag.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)).thenReturn(true);
         mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                 mock(NotificationIconAreaController.class),
@@ -116,7 +116,7 @@
                 mKeyguardStateController,
                 mCommandQueue,
                 mStackScrollerController,
-                mPanelView,
+                mShadeViewController,
                 mNotificationRoundnessManager,
                 mFeatureFlag,
                 mHeadsUpStatusBarView,
@@ -200,7 +200,7 @@
                 mKeyguardStateController,
                 mCommandQueue,
                 mStackScrollerController,
-                mPanelView,
+                mShadeViewController,
                 mNotificationRoundnessManager,
                 mFeatureFlag,
                 mHeadsUpStatusBarView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 760a90b..e838a480 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -52,7 +52,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -123,14 +123,14 @@
 
     @Mock private NotificationMediaManager mNotificationMediaManager;
 
-    private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
+    private TestShadeViewStateProvider mShadeViewStateProvider;
     private KeyguardStatusBarView mKeyguardStatusBarView;
     private KeyguardStatusBarViewController mController;
     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
     public void setup() throws Exception {
-        mNotificationPanelViewStateProvider = new TestNotificationPanelViewStateProvider();
+        mShadeViewStateProvider = new TestShadeViewStateProvider();
 
         MockitoAnnotations.initMocks(this);
 
@@ -158,7 +158,7 @@
                 mStatusBarIconController,
                 mIconManagerFactory,
                 mBatteryMeterViewController,
-                mNotificationPanelViewStateProvider,
+                mShadeViewStateProvider,
                 mKeyguardStateController,
                 mKeyguardBypassController,
                 mKeyguardUpdateMonitor,
@@ -358,7 +358,7 @@
         mController.onViewAttached();
         updateStateToKeyguard();
 
-        mNotificationPanelViewStateProvider.setPanelViewExpandedHeight(0);
+        mShadeViewStateProvider.setPanelViewExpandedHeight(0);
 
         mController.updateViewState();
 
@@ -370,7 +370,7 @@
         mController.onViewAttached();
         updateStateToKeyguard();
 
-        mNotificationPanelViewStateProvider.setLockscreenShadeDragProgress(1f);
+        mShadeViewStateProvider.setLockscreenShadeDragProgress(1f);
 
         mController.updateViewState();
 
@@ -451,7 +451,7 @@
         updateStateToKeyguard();
         mKeyguardStatusBarView.setVisibility(View.VISIBLE);
 
-        mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(true);
+        mShadeViewStateProvider.setShouldHeadsUpBeVisible(true);
         mController.updateForHeadsUp(/* animate= */ false);
 
         assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
@@ -463,10 +463,10 @@
         updateStateToKeyguard();
 
         // Start with the opposite state.
-        mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(true);
+        mShadeViewStateProvider.setShouldHeadsUpBeVisible(true);
         mController.updateForHeadsUp(/* animate= */ false);
 
-        mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(false);
+        mShadeViewStateProvider.setShouldHeadsUpBeVisible(false);
         mController.updateForHeadsUp(/* animate= */ false);
 
         assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
@@ -591,10 +591,10 @@
         return captor.getValue();
     }
 
-    private static class TestNotificationPanelViewStateProvider
-            implements NotificationPanelViewController.NotificationPanelViewStateProvider {
+    private static class TestShadeViewStateProvider
+            implements ShadeViewStateProvider {
 
-        TestNotificationPanelViewStateProvider() {}
+        TestShadeViewStateProvider() {}
 
         private float mPanelViewExpandedHeight = 100f;
         private boolean mShouldHeadsUpBeVisible = false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 7d9c091..8e1dcf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -27,6 +27,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationListener;
@@ -75,6 +76,8 @@
     @Mock private DemoModeController mDemoModeController;
     @Mock
     private NotificationIconContainer mAodIcons;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
     @Before
     public void setup() {
@@ -91,6 +94,7 @@
                 Optional.of(mBubbles),
                 mDemoModeController,
                 mDarkIconDispatcher,
+                mFeatureFlags,
                 mStatusBarWindowController,
                 mScreenOffAnimationController);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 3edf33b..2d96e59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -27,9 +27,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.shade.NotificationPanelViewController
 import com.android.systemui.shade.ShadeControllerImpl
 import com.android.systemui.shade.ShadeLogger
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
@@ -43,11 +43,11 @@
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 import java.util.Optional
 
@@ -55,7 +55,7 @@
 class PhoneStatusBarViewControllerTest : SysuiTestCase() {
 
     @Mock
-    private lateinit var notificationPanelViewController: NotificationPanelViewController
+    private lateinit var shadeViewController: ShadeViewController
     @Mock
     private lateinit var featureFlags: FeatureFlags
     @Mock
@@ -134,58 +134,58 @@
         val returnVal = view.onTouchEvent(
                         MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
         assertThat(returnVal).isFalse()
-        verify(notificationPanelViewController, never()).handleExternalTouch(any())
+        verify(shadeViewController, never()).handleExternalTouch(any())
     }
 
     @Test
     fun handleTouchEventFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
-        `when`(centralSurfacesImpl.notificationPanelViewController)
-                .thenReturn(notificationPanelViewController)
-        `when`(notificationPanelViewController.isViewEnabled).thenReturn(false)
+        `when`(centralSurfacesImpl.shadeViewController)
+                .thenReturn(shadeViewController)
+        `when`(shadeViewController.isViewEnabled).thenReturn(false)
         val returnVal = view.onTouchEvent(
                 MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
         assertThat(returnVal).isTrue()
-        verify(notificationPanelViewController, never()).handleExternalTouch(any())
+        verify(shadeViewController, never()).handleExternalTouch(any())
     }
 
     @Test
     fun handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
-        `when`(centralSurfacesImpl.notificationPanelViewController)
-                .thenReturn(notificationPanelViewController)
-        `when`(notificationPanelViewController.isViewEnabled).thenReturn(false)
+        `when`(centralSurfacesImpl.shadeViewController)
+                .thenReturn(shadeViewController)
+        `when`(shadeViewController.isViewEnabled).thenReturn(false)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
 
         view.onTouchEvent(event)
 
-        verify(notificationPanelViewController).handleExternalTouch(event)
+        verify(shadeViewController).handleExternalTouch(event)
     }
 
     @Test
     fun handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
-        `when`(centralSurfacesImpl.notificationPanelViewController)
-                .thenReturn(notificationPanelViewController)
-        `when`(notificationPanelViewController.isViewEnabled).thenReturn(true)
+        `when`(centralSurfacesImpl.shadeViewController)
+                .thenReturn(shadeViewController)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
 
         view.onTouchEvent(event)
 
-        verify(notificationPanelViewController).handleExternalTouch(event)
+        verify(shadeViewController).handleExternalTouch(event)
     }
 
     @Test
     fun handleTouchEventFromStatusBar_topEdgeTouch_viewNeverReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
-        `when`(centralSurfacesImpl.notificationPanelViewController)
-                .thenReturn(notificationPanelViewController)
-        `when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+        `when`(centralSurfacesImpl.shadeViewController)
+                .thenReturn(shadeViewController)
+        `when`(shadeViewController.isFullyCollapsed).thenReturn(true)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
 
         view.onTouchEvent(event)
 
-        verify(notificationPanelViewController, never()).handleExternalTouch(any())
+        verify(shadeViewController, never()).handleExternalTouch(any())
     }
 
     private fun createViewMock(): PhoneStatusBarView {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 3ed454f..9c10131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.shade.ShadeViewController
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -32,7 +32,7 @@
 class PhoneStatusBarViewTest : SysuiTestCase() {
 
     @Mock
-    private lateinit var notificationPanelViewController: NotificationPanelViewController
+    private lateinit var shadeViewController: ShadeViewController
     @Mock
     private lateinit var panelView: ViewGroup
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 14aee4e..4ff225c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -73,11 +73,11 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.TaskbarDelegate;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -110,7 +110,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private CentralSurfaces mCentralSurfaces;
     @Mock private ViewGroup mContainer;
-    @Mock private NotificationPanelViewController mNotificationPanelView;
+    @Mock private ShadeViewController mShadeViewController;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -202,7 +202,7 @@
                 .thenReturn(mOnBackInvokedDispatcher);
         mStatusBarKeyguardViewManager.registerCentralSurfaces(
                 mCentralSurfaces,
-                mNotificationPanelView,
+                mShadeViewController,
                 new ShadeExpansionStateManager(),
                 mBiometricUnlockController,
                 mNotificationContainer,
@@ -250,7 +250,7 @@
 
     @Test
     public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
-        when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true);
+        when(mShadeViewController.isUnlockHintRunning()).thenReturn(true);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
         verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index ee7e082..d6ae2b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -66,9 +66,9 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
 import com.android.systemui.shade.ShadeControllerImpl;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -232,7 +232,7 @@
                         mOnUserInteractionCallback,
                         mCentralSurfaces,
                         mock(NotificationPresenter.class),
-                        mock(NotificationPanelViewController.class),
+                        mock(ShadeViewController.class),
                         mActivityLaunchAnimator,
                         notificationAnimationProvider,
                         mock(LaunchFullScreenIntentProvider.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index e83e50d..fdfe028 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -25,15 +25,12 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
-import android.metrics.LogMaker;
-import android.support.test.metricshelper.MetricsAsserts;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
@@ -41,11 +38,11 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeNotificationPresenter;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -61,7 +58,6 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -111,12 +107,12 @@
                 mock(NotificationStackScrollLayout.class));
         when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
 
-        NotificationPanelViewController npvc = mock(NotificationPanelViewController.class);
-        when(npvc.getShadeNotificationPresenter())
+        ShadeViewController shadeViewController = mock(ShadeViewController.class);
+        when(shadeViewController.getShadeNotificationPresenter())
                 .thenReturn(mock(ShadeNotificationPresenter.class));
         mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
                 mContext,
-                npvc,
+                shadeViewController,
                 mock(QuickSettingsController.class),
                 mock(HeadsUpManagerPhone.class),
                 notificationShadeWindowView,
@@ -198,46 +194,7 @@
     }
 
     @Test
-    public void testNoSuppressHeadsUp_FSI_occludedKeygaurd() {
-        when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
-        Notification n = new Notification.Builder(getContext(), "a")
-                .setFullScreenIntent(mock(PendingIntent.class), true)
-                .build();
-        NotificationEntry entry = new NotificationEntryBuilder()
-                .setPkg("a")
-                .setOpPkg("a")
-                .setTag("a")
-                .setNotification(n)
-                .build();
-
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        when(mCentralSurfaces.isOccluded()).thenReturn(true);
-        assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
-    }
-
-    @Test
-    public void testSuppressHeadsUp_FSI_nonOccludedKeygaurd() {
-        when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
-        Notification n = new Notification.Builder(getContext(), "a")
-                .setFullScreenIntent(mock(PendingIntent.class), true)
-                .build();
-        NotificationEntry entry = new NotificationEntryBuilder()
-                .setPkg("a")
-                .setOpPkg("a")
-                .setTag("a")
-                .setNotification(n)
-                .build();
-
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isOccluded()).thenReturn(false);
-        when(mCentralSurfaces.isOccluded()).thenReturn(false);
-        assertTrue(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
-    }
-
-    @Test
-    public void testNoSuppressHeadsUp_FSI_nonOccludedKeygaurd_withNewFlag() {
-        when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+    public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard() {
         Notification n = new Notification.Builder(getContext(), "a")
                 .setFullScreenIntent(mock(PendingIntent.class), true)
                 .build();
@@ -283,15 +240,4 @@
         assertTrue("CentralSurfaces alerts disabled shouldn't allow interruptions",
                 mInterruptSuppressor.suppressInterruptions(entry));
     }
-
-    @Test
-    public void onActivatedMetrics() {
-        ActivatableNotificationView view =  mock(ActivatableNotificationView.class);
-        mStatusBarNotificationPresenter.onActivated(view);
-
-        MetricsAsserts.assertHasLog("missing lockscreen note tap log",
-                mMetricsLogger.getLogs(),
-                new LogMaker(MetricsEvent.ACTION_LS_NOTE)
-                        .setType(MetricsEvent.TYPE_ACTION));
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 02c459b..3c644a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.StatusBarStateControllerImpl
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -59,9 +59,9 @@
     @Mock
     private lateinit var globalSettings: GlobalSettings
     @Mock
-    private lateinit var mCentralSurfaces: CentralSurfaces
+    private lateinit var centralSurfaces: CentralSurfaces
     @Mock
-    private lateinit var notificationPanelViewController: NotificationPanelViewController
+    private lateinit var shadeViewController: ShadeViewController
     @Mock
     private lateinit var lightRevealScrim: LightRevealScrim
     @Mock
@@ -91,13 +91,13 @@
                 powerManager,
                 handler = handler
         )
-        controller.initialize(mCentralSurfaces, lightRevealScrim)
-        `when`(mCentralSurfaces.notificationPanelViewController).thenReturn(
-            notificationPanelViewController)
+        controller.initialize(centralSurfaces, lightRevealScrim)
+        `when`(centralSurfaces.shadeViewController).thenReturn(
+            shadeViewController)
 
         // Screen off does not run if the panel is expanded, so we should say it's collapsed to test
         // screen off.
-        `when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+        `when`(shadeViewController.isFullyCollapsed).thenReturn(true)
     }
 
     @After
@@ -127,7 +127,7 @@
 
         callbackCaptor.value.run()
 
-        verify(notificationPanelViewController, times(1)).showAodUi()
+        verify(shadeViewController, times(1)).showAodUi()
     }
 
     /**
@@ -149,7 +149,7 @@
         verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
         callbackCaptor.value.run()
 
-        verify(notificationPanelViewController, never()).showAodUi()
+        verify(shadeViewController, never()).showAodUi()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index be0c83f..2a3c775 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -56,8 +56,8 @@
 import com.android.systemui.plugins.log.LogBuffer;
 import com.android.systemui.plugins.log.LogcatEchoTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
@@ -117,7 +117,7 @@
     @Mock
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     @Mock
-    private NotificationPanelViewController mNotificationPanelViewController;
+    private ShadeViewController mShadeViewController;
     @Mock
     private StatusBarIconController.DarkIconManager.Factory mIconManagerFactory;
     @Mock
@@ -507,7 +507,7 @@
                 mIconManagerFactory,
                 mStatusBarHideIconsForBouncerManager,
                 mKeyguardStateController,
-                mNotificationPanelViewController,
+                mShadeViewController,
                 mStatusBarStateController,
                 mCommandQueue,
                 mCarrierConfigTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
index 63cb30c..95b132d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -39,7 +39,7 @@
     }
 
     @Test
-    fun `process new config - reflected by isUsingDefault`() {
+    fun processNewConfig_reflectedByIsUsingDefault() {
         // Starts out using the defaults
         assertThat(underTest.isUsingDefault).isTrue()
 
@@ -50,7 +50,7 @@
     }
 
     @Test
-    fun `process new config - updates all flows`() {
+    fun processNewConfig_updatesAllFlows() {
         assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
         assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
 
@@ -66,7 +66,7 @@
     }
 
     @Test
-    fun `process new config - defaults to false for config overrides`() {
+    fun processNewConfig_defaultsToFalseForConfigOverrides() {
         // This case is only apparent when:
         //   1. The default is true
         //   2. The override config has no value for a given key
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index dfef62e..6e3af26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -96,7 +96,7 @@
     }
 
     @Test
-    fun `carrier config stream produces int-bundle pairs`() =
+    fun carrierConfigStreamProducesIntBundlePairs() =
         testScope.runTest {
             var latest: Pair<Int, PersistableBundle>? = null
             val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
@@ -111,7 +111,7 @@
         }
 
     @Test
-    fun `carrier config stream ignores invalid subscriptions`() =
+    fun carrierConfigStreamIgnoresInvalidSubscriptions() =
         testScope.runTest {
             var latest: Pair<Int, PersistableBundle>? = null
             val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
@@ -124,19 +124,19 @@
         }
 
     @Test
-    fun `getOrCreateConfig - uses default config if no override`() {
+    fun getOrCreateConfig_usesDefaultConfigIfNoOverride() {
         val config = underTest.getOrCreateConfigForSubId(123)
         assertThat(config.isUsingDefault).isTrue()
     }
 
     @Test
-    fun `getOrCreateConfig - uses override if exists`() {
+    fun getOrCreateConfig_usesOverrideIfExists() {
         val config = underTest.getOrCreateConfigForSubId(SUB_ID_1)
         assertThat(config.isUsingDefault).isFalse()
     }
 
     @Test
-    fun `config - updates while config stream is collected`() =
+    fun config_updatesWhileConfigStreamIsCollected() =
         testScope.runTest {
             CONFIG_1.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 1fdcf7f..3ec9690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -156,7 +156,7 @@
     }
 
     @Test
-    fun `active repo matches demo mode setting`() =
+    fun activeRepoMatchesDemoModeSetting() =
         runBlocking(IMMEDIATE) {
             whenever(demoModeController.isInDemoMode).thenReturn(false)
 
@@ -177,7 +177,7 @@
         }
 
     @Test
-    fun `subscription list updates when demo mode changes`() =
+    fun subscriptionListUpdatesWhenDemoModeChanges() =
         runBlocking(IMMEDIATE) {
             whenever(demoModeController.isInDemoMode).thenReturn(false)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 47f8cd3..1251dfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -105,7 +105,7 @@
         }
 
     @Test
-    fun `network event - create new subscription`() =
+    fun networkEvent_createNewSubscription() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -121,7 +121,7 @@
         }
 
     @Test
-    fun `wifi carrier merged event - create new subscription`() =
+    fun wifiCarrierMergedEvent_createNewSubscription() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -137,7 +137,7 @@
         }
 
     @Test
-    fun `network event - reuses subscription when same Id`() =
+    fun networkEvent_reusesSubscriptionWhenSameId() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -159,7 +159,7 @@
         }
 
     @Test
-    fun `wifi carrier merged event - reuses subscription when same Id`() =
+    fun wifiCarrierMergedEvent_reusesSubscriptionWhenSameId() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -181,7 +181,7 @@
         }
 
     @Test
-    fun `multiple subscriptions`() =
+    fun multipleSubscriptions() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -195,7 +195,7 @@
         }
 
     @Test
-    fun `mobile subscription and carrier merged subscription`() =
+    fun mobileSubscriptionAndCarrierMergedSubscription() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -209,7 +209,7 @@
         }
 
     @Test
-    fun `multiple mobile subscriptions and carrier merged subscription`() =
+    fun multipleMobileSubscriptionsAndCarrierMergedSubscription() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -224,7 +224,7 @@
         }
 
     @Test
-    fun `mobile disabled event - disables connection - subId specified - single conn`() =
+    fun mobileDisabledEvent_disablesConnection_subIdSpecified_singleConn() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -239,7 +239,7 @@
         }
 
     @Test
-    fun `mobile disabled event - disables connection - subId not specified - single conn`() =
+    fun mobileDisabledEvent_disablesConnection_subIdNotSpecified_singleConn() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -254,7 +254,7 @@
         }
 
     @Test
-    fun `mobile disabled event - disables connection - subId specified - multiple conn`() =
+    fun mobileDisabledEvent_disablesConnection_subIdSpecified_multipleConn() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -270,7 +270,7 @@
         }
 
     @Test
-    fun `mobile disabled event - subId not specified - multiple conn - ignores command`() =
+    fun mobileDisabledEvent_subIdNotSpecified_multipleConn_ignoresCommand() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -286,7 +286,7 @@
         }
 
     @Test
-    fun `wifi network updates to disabled - carrier merged connection removed`() =
+    fun wifiNetworkUpdatesToDisabled_carrierMergedConnectionRemoved() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -303,7 +303,7 @@
         }
 
     @Test
-    fun `wifi network updates to active - carrier merged connection removed`() =
+    fun wifiNetworkUpdatesToActive_carrierMergedConnectionRemoved() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -326,7 +326,7 @@
         }
 
     @Test
-    fun `mobile sub updates to carrier merged - only one connection`() =
+    fun mobileSubUpdatesToCarrierMerged_onlyOneConnection() =
         testScope.runTest {
             var latestSubsList: List<SubscriptionModel>? = null
             var connections: List<DemoMobileConnectionRepository>? = null
@@ -352,7 +352,7 @@
         }
 
     @Test
-    fun `mobile sub updates to carrier merged then back - has old mobile data`() =
+    fun mobileSubUpdatesToCarrierMergedThenBack_hasOldMobileData() =
         testScope.runTest {
             var latestSubsList: List<SubscriptionModel>? = null
             var connections: List<DemoMobileConnectionRepository>? = null
@@ -393,7 +393,7 @@
 
     /** Regression test for b/261706421 */
     @Test
-    fun `multiple connections - remove all - does not throw`() =
+    fun multipleConnections_removeAll_doesNotThrow() =
         testScope.runTest {
             var latest: List<SubscriptionModel>? = null
             val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -411,7 +411,7 @@
         }
 
     @Test
-    fun `demo connection - single subscription`() =
+    fun demoConnection_singleSubscription() =
         testScope.runTest {
             var currentEvent: FakeNetworkEventModel = validMobileEvent(subId = 1)
             var connections: List<DemoMobileConnectionRepository>? = null
@@ -440,7 +440,7 @@
         }
 
     @Test
-    fun `demo connection - two connections - update second - no affect on first`() =
+    fun demoConnection_twoConnections_updateSecond_noAffectOnFirst() =
         testScope.runTest {
             var currentEvent1 = validMobileEvent(subId = 1)
             var connection1: DemoMobileConnectionRepository? = null
@@ -487,7 +487,7 @@
         }
 
     @Test
-    fun `demo connection - two connections - update carrier merged - no affect on first`() =
+    fun demoConnection_twoConnections_updateCarrierMerged_noAffectOnFirst() =
         testScope.runTest {
             var currentEvent1 = validMobileEvent(subId = 1)
             var connection1: DemoMobileConnectionRepository? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 423c476..9f77744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -292,7 +292,7 @@
         }
 
     @Test
-    fun `factory - reuses log buffers for same connection`() =
+    fun factory_reusesLogBuffersForSameConnection() =
         testScope.runTest {
             val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
 
@@ -327,7 +327,7 @@
         }
 
     @Test
-    fun `factory - reuses log buffers for same sub ID even if carrier merged`() =
+    fun factory_reusesLogBuffersForSameSubIDevenIfCarrierMerged() =
         testScope.runTest {
             val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 8d7f0f6..c276865 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -353,7 +353,7 @@
         }
 
     @Test
-    fun `isInService - uses repository value`() =
+    fun isInService_usesRepositoryValue() =
         testScope.runTest {
             var latest: Boolean? = null
             val job = underTest.isInService.onEach { latest = it }.launchIn(this)
@@ -370,7 +370,7 @@
         }
 
     @Test
-    fun `roaming - is gsm - uses connection model`() =
+    fun roaming_isGsm_usesConnectionModel() =
         testScope.runTest {
             var latest: Boolean? = null
             val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -389,7 +389,7 @@
         }
 
     @Test
-    fun `roaming - is cdma - uses cdma roaming bit`() =
+    fun roaming_isCdma_usesCdmaRoamingBit() =
         testScope.runTest {
             var latest: Boolean? = null
             val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -410,7 +410,7 @@
         }
 
     @Test
-    fun `roaming - false while carrierNetworkChangeActive`() =
+    fun roaming_falseWhileCarrierNetworkChangeActive() =
         testScope.runTest {
             var latest: Boolean? = null
             val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -431,7 +431,7 @@
         }
 
     @Test
-    fun `network name - uses operatorAlphaShot when non null and repo is default`() =
+    fun networkName_usesOperatorAlphaShotWhenNonNullAndRepoIsDefault() =
         testScope.runTest {
             var latest: NetworkNameModel? = null
             val job = underTest.networkName.onEach { latest = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index dc68386..c84c9c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -520,7 +520,7 @@
     // is private and can only be tested by looking at [isDefaultConnectionFailed].
 
     @Test
-    fun `data switch - in same group - validated matches previous value - expires after 2s`() =
+    fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
         testScope.runTest {
             var latestConnectionFailed: Boolean? = null
             val job =
@@ -548,7 +548,7 @@
         }
 
     @Test
-    fun `data switch - in same group - not validated - immediately marked as failed`() =
+    fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
         testScope.runTest {
             var latestConnectionFailed: Boolean? = null
             val job =
@@ -567,7 +567,7 @@
         }
 
     @Test
-    fun `data switch - lose validation - then switch happens - clears forced bit`() =
+    fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
         testScope.runTest {
             var latestConnectionFailed: Boolean? = null
             val job =
@@ -602,7 +602,7 @@
         }
 
     @Test
-    fun `data switch - while already forcing validation - resets clock`() =
+    fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
         testScope.runTest {
             var latestConnectionFailed: Boolean? = null
             val job =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index e99be86..d5fb577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -92,7 +92,7 @@
     }
 
     @Test
-    fun `location based view models receive same icon id when common impl updates`() =
+    fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() =
         testScope.runTest {
             var latestHome: SignalIconModel? = null
             val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 297cb9d..2b7bc78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -249,7 +249,7 @@
         }
 
     @Test
-    fun `icon - uses empty state - when not in service`() =
+    fun icon_usesEmptyState_whenNotInService() =
         testScope.runTest {
             var latest: SignalIconModel? = null
             val job = underTest.icon.onEach { latest = it }.launchIn(this)
@@ -480,7 +480,7 @@
         }
 
     @Test
-    fun `network type - alwaysShow - shown when not default`() =
+    fun networkType_alwaysShow_shownWhenNotDefault() =
         testScope.runTest {
             interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
             interactor.mobileIsDefault.value = false
@@ -500,7 +500,7 @@
         }
 
     @Test
-    fun `network type - not shown when not default`() =
+    fun networkType_notShownWhenNotDefault() =
         testScope.runTest {
             interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
             interactor.isDataConnected.value = true
@@ -531,7 +531,7 @@
         }
 
     @Test
-    fun `data activity - null when config is off`() =
+    fun dataActivity_nullWhenConfigIsOff() =
         testScope.runTest {
             // Create a new view model here so the constants are properly read
             whenever(constants.shouldShowActivityConfig).thenReturn(false)
@@ -563,7 +563,7 @@
         }
 
     @Test
-    fun `data activity - config on - test indicators`() =
+    fun dataActivity_configOn_testIndicators() =
         testScope.runTest {
             // Create a new view model here so the constants are properly read
             whenever(constants.shouldShowActivityConfig).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index f8e1aa9..f0458fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -111,7 +111,7 @@
         }
 
     @Test
-    fun `caching - mobile icon view model is reused for same sub id`() =
+    fun caching_mobileIconViewModelIsReusedForSameSubId() =
         testScope.runTest {
             val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
             val model2 = underTest.viewModelForSub(1, StatusBarLocation.QS)
@@ -120,7 +120,7 @@
         }
 
     @Test
-    fun `caching - invalid view models are removed from cache when sub disappears`() =
+    fun caching_invalidViewModelsAreRemovedFromCacheWhenSubDisappears() =
         testScope.runTest {
             // Retrieve models to trigger caching
             val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index 70d2d2b..30b95ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -105,7 +105,7 @@
     }
 
     @Test
-    fun `switcher active repo - updates when demo mode changes`() =
+    fun switcherActiveRepo_updatesWhenDemoModeChanges() =
         testScope.runTest {
             assertThat(underTest.activeRepo.value).isSameInstanceAs(realImpl)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
index 0a3da0b..67727ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
@@ -87,7 +87,7 @@
     }
 
     @Test
-    fun `Adds self to controller in constructor`() {
+    fun addsSelfToControllerInConstructor() {
         val captor = kotlinArgumentCaptor<WeakReference<BaseUserSwitcherAdapter>>()
         verify(controller).addAdapter(captor.capture())
 
@@ -100,7 +100,7 @@
     }
 
     @Test
-    fun `count - ignores restricted users when device is locked`() {
+    fun count_ignoresRestrictedUsersWhenDeviceIsLocked() {
         whenever(controller.isKeyguardShowing).thenReturn(true)
         users =
             ArrayList(
@@ -131,7 +131,7 @@
     }
 
     @Test
-    fun `count - does not ignore restricted users when device is not locked`() {
+    fun count_doesNotIgnoreRestrictedUsersWhenDeviceIsNotLocked() {
         whenever(controller.isKeyguardShowing).thenReturn(false)
         users =
             ArrayList(
@@ -185,7 +185,7 @@
     }
 
     @Test
-    fun `getName - non guest - returns real name`() {
+    fun getName_nonGuest_returnsRealName() {
         val userRecord =
             createUserRecord(
                 id = 1,
@@ -196,7 +196,7 @@
     }
 
     @Test
-    fun `getName - guest and selected - returns exit guest action name`() {
+    fun getName_guestAndSelected_returnsExitGuestActionName() {
         val expected = "Exit guest"
         context.orCreateTestableResources.addOverride(
             com.android.settingslib.R.string.guest_exit_quick_settings_button,
@@ -215,7 +215,7 @@
     }
 
     @Test
-    fun `getName - guest and not selected - returns enter guest action name`() {
+    fun getName_guestAndNotSelected_returnsEnterGuestActionName() {
         val expected = "Guest"
         context.orCreateTestableResources.addOverride(
             com.android.internal.R.string.guest_name,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 391c8ca..50bb058 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -70,8 +70,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -453,17 +451,13 @@
     private RemoteInputViewController bindController(
             RemoteInputView view,
             NotificationEntry entry) {
-        FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
-        fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
         RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
                 view,
                 entry,
                 mRemoteInputQuickSettingsDisabler,
                 mController,
                 mShortcutManager,
-                mUiEventLoggerFake,
-                fakeFeatureFlags
-                );
+                mUiEventLoggerFake);
         viewController.bind();
         return viewController;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index 17f8ec2..0b3dd66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -101,9 +101,12 @@
         whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
         whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
 
+        whenever(btStylusDevice.isExternal).thenReturn(true)
+
         whenever(stylusDevice.bluetoothAddress).thenReturn(null)
         whenever(btStylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS)
 
+        whenever(btStylusDevice.batteryState).thenReturn(batteryState)
         whenever(stylusDevice.batteryState).thenReturn(batteryState)
         whenever(batteryState.capacity).thenReturn(0.5f)
 
@@ -148,6 +151,27 @@
     }
 
     @Test
+    fun startListener_hasNotStarted_registersExistingBluetoothDevice() {
+        whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(BT_STYLUS_DEVICE_ID))
+
+        stylusManager =
+            StylusManager(
+                mContext,
+                inputManager,
+                bluetoothAdapter,
+                handler,
+                EXECUTOR,
+                featureFlags,
+                uiEventLogger
+            )
+
+        stylusManager.startListener()
+
+        verify(bluetoothAdapter, times(1))
+            .addOnMetadataChangedListener(bluetoothDevice, EXECUTOR, stylusManager)
+    }
+
+    @Test
     fun startListener_hasStarted_doesNothing() {
         stylusManager.startListener()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index dfbd61b..8fc0a1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.shade.NotificationPanelViewController
 import com.android.systemui.shade.ShadeFoldAnimator
+import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -74,7 +74,7 @@
 
     @Mock lateinit var lightRevealScrim: LightRevealScrim
 
-    @Mock lateinit var notificationPanelViewController: NotificationPanelViewController
+    @Mock lateinit var shadeViewController: ShadeViewController
 
     @Mock lateinit var viewGroup: ViewGroup
 
@@ -100,13 +100,12 @@
         deviceStates = FoldableTestUtils.findDeviceStates(context)
 
         // TODO(b/254878364): remove this call to NPVC.getView()
-        whenever(notificationPanelViewController.shadeFoldAnimator).thenReturn(shadeFoldAnimator)
+        whenever(shadeViewController.shadeFoldAnimator).thenReturn(shadeFoldAnimator)
         whenever(shadeFoldAnimator.view).thenReturn(viewGroup)
         whenever(viewGroup.viewTreeObserver).thenReturn(viewTreeObserver)
         whenever(wakefulnessLifecycle.lastSleepReason)
             .thenReturn(PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD)
-        whenever(centralSurfaces.notificationPanelViewController)
-            .thenReturn(notificationPanelViewController)
+        whenever(centralSurfaces.shadeViewController).thenReturn(shadeViewController)
         whenever(shadeFoldAnimator.startFoldToAodAnimation(any(), any(), any())).then {
             val onActionStarted = it.arguments[0] as Runnable
             onActionStarted.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
index f14009aa..70eadce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
@@ -39,16 +39,36 @@
     }
 
     @Test
-    fun onTransitionProgress_withInterval_propagated() {
-        runOnMainThreadWithInterval(
-            { progressProvider.onTransitionStarted() },
-            { progressProvider.onTransitionProgress(0.5f) }
-        )
+    fun onTransitionProgress_firstProgressEvent_propagatedImmediately() {
+        progressProvider.onTransitionStarted()
+        progressProvider.onTransitionProgress(0.5f)
 
         listener.assertLastProgress(0.5f)
     }
 
     @Test
+    fun onTransitionProgress_secondProgressEvent_isNotPropagatedImmediately() =
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            progressProvider.onTransitionStarted()
+            progressProvider.onTransitionProgress(0.5f)
+            progressProvider.onTransitionProgress(0.8f)
+
+            // 0.8f should be set only later, after the animation
+            listener.assertLastProgress(0.5f)
+        }
+
+    @Test
+    fun onTransitionProgress_severalProgressEventsWithInterval_propagated() {
+        runOnMainThreadWithInterval(
+            { progressProvider.onTransitionStarted() },
+            { progressProvider.onTransitionProgress(0.5f) },
+            { progressProvider.onTransitionProgress(0.8f) }
+        )
+
+        listener.assertLastProgress(0.8f)
+    }
+
+    @Test
     fun onTransitionEnded_propagated() {
         runOnMainThreadWithInterval(
             { progressProvider.onTransitionStarted() },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index e2f3cf7..079fbcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -164,7 +164,7 @@
     }
 
     @Test
-    fun `refreshUsers - sorts by creation time - guest user last`() = runSelfCancelingTest {
+    fun refreshUsers_sortsByCreationTime_guestUserLast() = runSelfCancelingTest {
         underTest = create(this)
         val unsortedUsers =
             setUpUsers(
@@ -205,7 +205,7 @@
         return userInfos
     }
     @Test
-    fun `userTrackerCallback - updates selectedUserInfo`() = runSelfCancelingTest {
+    fun userTrackerCallback_updatesSelectedUserInfo() = runSelfCancelingTest {
         underTest = create(this)
         var selectedUserInfo: UserInfo? = null
         underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 0c119fd..948670f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -97,13 +97,13 @@
     }
 
     @Test
-    fun `registers broadcast receivers`() {
+    fun registersBroadcastReceivers() {
         verify(resumeSessionReceiver).register()
         verify(resetOrExitSessionReceiver).register()
     }
 
     @Test
-    fun `onDeviceBootCompleted - allowed to add - create guest`() =
+    fun onDeviceBootCompleted_allowedToAdd_createGuest() =
         runBlocking(IMMEDIATE) {
             setAllowedToAdd()
 
@@ -114,7 +114,7 @@
         }
 
     @Test
-    fun `onDeviceBootCompleted - await provisioning - and create guest`() =
+    fun onDeviceBootCompleted_awaitProvisioning_andCreateGuest() =
         runBlocking(IMMEDIATE) {
             setAllowedToAdd(isAllowed = false)
             underTest.onDeviceBootCompleted()
@@ -145,7 +145,7 @@
         }
 
     @Test
-    fun `createAndSwitchTo - fails to create - does not switch to`() =
+    fun createAndSwitchTo_failsToCreate_doesNotSwitchTo() =
         runBlocking(IMMEDIATE) {
             whenever(manager.createGuest(any())).thenReturn(null)
 
@@ -162,7 +162,7 @@
         }
 
     @Test
-    fun `exit - returns to target user`() =
+    fun exit_returnsToTargetUser() =
         runBlocking(IMMEDIATE) {
             repository.setSelectedUserInfo(GUEST_USER_INFO)
 
@@ -182,7 +182,7 @@
         }
 
     @Test
-    fun `exit - returns to last non-guest`() =
+    fun exit_returnsToLastNonGuest() =
         runBlocking(IMMEDIATE) {
             val expectedUserId = NON_GUEST_USER_INFO.id
             whenever(manager.getUserInfo(expectedUserId)).thenReturn(NON_GUEST_USER_INFO)
@@ -204,7 +204,7 @@
         }
 
     @Test
-    fun `exit - last non-guest was removed - returns to main user`() =
+    fun exit_lastNonGuestWasRemoved_returnsToMainUser() =
         runBlocking(IMMEDIATE) {
             val removedUserId = 310
             val mainUserId = 10
@@ -227,7 +227,7 @@
         }
 
     @Test
-    fun `exit - guest was ephemeral - it is removed`() =
+    fun exit_guestWasEphemeral_itIsRemoved() =
         runBlocking(IMMEDIATE) {
             whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
             repository.setUserInfos(listOf(NON_GUEST_USER_INFO, EPHEMERAL_GUEST_USER_INFO))
@@ -250,7 +250,7 @@
         }
 
     @Test
-    fun `exit - force remove guest - it is removed`() =
+    fun exit_forceRemoveGuest_itIsRemoved() =
         runBlocking(IMMEDIATE) {
             whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
             repository.setSelectedUserInfo(GUEST_USER_INFO)
@@ -272,7 +272,7 @@
         }
 
     @Test
-    fun `exit - selected different from guest user - do nothing`() =
+    fun exit_selectedDifferentFromGuestUser_doNothing() =
         runBlocking(IMMEDIATE) {
             repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
 
@@ -289,7 +289,7 @@
         }
 
     @Test
-    fun `exit - selected is actually not a guest user - do nothing`() =
+    fun exit_selectedIsActuallyNotAguestUser_doNothing() =
         runBlocking(IMMEDIATE) {
             repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
 
@@ -306,7 +306,7 @@
         }
 
     @Test
-    fun `remove - returns to target user`() =
+    fun remove_returnsToTargetUser() =
         runBlocking(IMMEDIATE) {
             whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
             repository.setSelectedUserInfo(GUEST_USER_INFO)
@@ -327,7 +327,7 @@
         }
 
     @Test
-    fun `remove - selected different from guest user - do nothing`() =
+    fun remove_selectedDifferentFromGuestUser_doNothing() =
         runBlocking(IMMEDIATE) {
             whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
             repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
@@ -344,7 +344,7 @@
         }
 
     @Test
-    fun `remove - selected is actually not a guest user - do nothing`() =
+    fun remove_selectedIsActuallyNotAguestUser_doNothing() =
         runBlocking(IMMEDIATE) {
             whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
             repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
index 593ce1f..b30f77a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
@@ -45,7 +45,7 @@
     }
 
     @Test
-    fun `pause - prevents the next refresh from happening`() =
+    fun pause_preventsTheNextRefreshFromHappening() =
         runBlocking(IMMEDIATE) {
             underTest =
                 RefreshUsersScheduler(
@@ -60,7 +60,7 @@
         }
 
     @Test
-    fun `unpauseAndRefresh - forces the refresh even when paused`() =
+    fun unpauseAndRefresh_forcesTheRefreshEvenWhenPaused() =
         runBlocking(IMMEDIATE) {
             underTest =
                 RefreshUsersScheduler(
@@ -76,7 +76,7 @@
         }
 
     @Test
-    fun `refreshIfNotPaused - refreshes when not paused`() =
+    fun refreshIfNotPaused_refreshesWhenNotPaused() =
         runBlocking(IMMEDIATE) {
             underTest =
                 RefreshUsersScheduler(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index adba538..d252d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -182,7 +182,7 @@
     }
 
     @Test
-    fun `testKeyguardUpdateMonitor_onKeyguardGoingAway`() =
+    fun testKeyguardUpdateMonitor_onKeyguardGoingAway() =
         testScope.runTest {
             val argumentCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
             verify(keyguardUpdateMonitor).registerCallback(argumentCaptor.capture())
@@ -194,7 +194,7 @@
         }
 
     @Test
-    fun `onRecordSelected - user`() =
+    fun onRecordSelected_user() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -211,7 +211,7 @@
         }
 
     @Test
-    fun `onRecordSelected - switch to guest user`() =
+    fun onRecordSelected_switchToGuestUser() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -227,7 +227,7 @@
         }
 
     @Test
-    fun `onRecordSelected - switch to restricted user`() =
+    fun onRecordSelected_switchToRestrictedUser() =
         testScope.runTest {
             var userInfos = createUserInfos(count = 2, includeGuest = false).toMutableList()
             userInfos.add(
@@ -252,7 +252,7 @@
         }
 
     @Test
-    fun `onRecordSelected - enter guest mode`() =
+    fun onRecordSelected_enterGuestMode() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -272,7 +272,7 @@
         }
 
     @Test
-    fun `onRecordSelected - action`() =
+    fun onRecordSelected_action() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -288,7 +288,7 @@
         }
 
     @Test
-    fun `users - switcher enabled`() =
+    fun users_switcherEnabled() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -301,7 +301,7 @@
         }
 
     @Test
-    fun `users - switches to second user`() =
+    fun users_switchesToSecondUser() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -315,7 +315,7 @@
         }
 
     @Test
-    fun `users - switcher not enabled`() =
+    fun users_switcherNotEnabled() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -342,7 +342,7 @@
         }
 
     @Test
-    fun `actions - device unlocked`() =
+    fun actions_deviceUnlocked() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
 
@@ -366,7 +366,7 @@
         }
 
     @Test
-    fun `actions - device unlocked - full screen`() =
+    fun actions_deviceUnlocked_fullScreen() =
         testScope.runTest {
             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
             val userInfos = createUserInfos(count = 2, includeGuest = false)
@@ -389,7 +389,7 @@
         }
 
     @Test
-    fun `actions - device unlocked user not primary - empty list`() =
+    fun actions_deviceUnlockedUserNotPrimary_emptyList() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -402,7 +402,7 @@
         }
 
     @Test
-    fun `actions - device unlocked user is guest - empty list`() =
+    fun actions_deviceUnlockedUserIsGuest_emptyList() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = true)
             assertThat(userInfos[1].isGuest).isTrue()
@@ -416,7 +416,7 @@
         }
 
     @Test
-    fun `actions - device locked add from lockscreen set - full list`() =
+    fun actions_deviceLockedAddFromLockscreenSet_fullList() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -442,7 +442,7 @@
         }
 
     @Test
-    fun `actions - device locked add from lockscreen set - full list - full screen`() =
+    fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() =
         testScope.runTest {
             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
             val userInfos = createUserInfos(count = 2, includeGuest = false)
@@ -469,7 +469,7 @@
         }
 
     @Test
-    fun `actions - device locked - only  manage user is shown`() =
+    fun actions_deviceLocked_onlymanageUserIsShown() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -482,7 +482,7 @@
         }
 
     @Test
-    fun `executeAction - add user - dialog shown`() =
+    fun executeAction_addUser_dialogShown() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -509,7 +509,7 @@
         }
 
     @Test
-    fun `executeAction - add supervised user - dismisses dialog and starts activity`() =
+    fun executeAction_addSupervisedUser_dismissesDialogAndStartsActivity() =
         testScope.runTest {
             underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
 
@@ -523,7 +523,7 @@
         }
 
     @Test
-    fun `executeAction - navigate to manage users`() =
+    fun executeAction_navigateToManageUsers() =
         testScope.runTest {
             underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
 
@@ -533,7 +533,7 @@
         }
 
     @Test
-    fun `executeAction - guest mode`() =
+    fun executeAction_guestMode() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -571,7 +571,7 @@
         }
 
     @Test
-    fun `selectUser - already selected guest re-selected - exit guest dialog`() =
+    fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = true)
             val guestUserInfo = userInfos[1]
@@ -592,7 +592,7 @@
         }
 
     @Test
-    fun `selectUser - currently guest non-guest selected - exit guest dialog`() =
+    fun selectUser_currentlyGuestNonGuestSelected_exitGuestDialog() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = true)
             val guestUserInfo = userInfos[1]
@@ -610,7 +610,7 @@
         }
 
     @Test
-    fun `selectUser - not currently guest - switches users`() =
+    fun selectUser_notCurrentlyGuest_switchesUsers() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -626,7 +626,7 @@
         }
 
     @Test
-    fun `Telephony call state changes - refreshes users`() =
+    fun telephonyCallStateChanges_refreshesUsers() =
         testScope.runTest {
             runCurrent()
 
@@ -639,7 +639,7 @@
         }
 
     @Test
-    fun `User switched broadcast`() =
+    fun userSwitchedBroadcast() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -670,7 +670,7 @@
         }
 
     @Test
-    fun `User info changed broadcast`() =
+    fun userInfoChangedBroadcast() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -690,7 +690,7 @@
         }
 
     @Test
-    fun `System user unlocked broadcast - refresh users`() =
+    fun systemUserUnlockedBroadcast_refreshUsers() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -710,7 +710,7 @@
         }
 
     @Test
-    fun `Non-system user unlocked broadcast - do not refresh users`() =
+    fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -799,7 +799,7 @@
         }
 
     @Test
-    fun `users - secondary user - guest user can be switched to`() =
+    fun users_secondaryUser_guestUserCanBeSwitchedTo() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -812,7 +812,7 @@
         }
 
     @Test
-    fun `users - secondary user - no guest action`() =
+    fun users_secondaryUser_noGuestAction() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -824,7 +824,7 @@
         }
 
     @Test
-    fun `users - secondary user - no guest user record`() =
+    fun users_secondaryUser_noGuestUserRecord() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -835,7 +835,7 @@
         }
 
     @Test
-    fun `show user switcher - full screen disabled - shows dialog switcher`() =
+    fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() =
         testScope.runTest {
             val expandable = mock<Expandable>()
             underTest.showUserSwitcher(expandable)
@@ -851,7 +851,7 @@
         }
 
     @Test
-    fun `show user switcher - full screen enabled - launches full screen dialog`() =
+    fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() =
         testScope.runTest {
             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
 
@@ -869,7 +869,7 @@
         }
 
     @Test
-    fun `users - secondary user - managed profile is not included`() =
+    fun users_secondaryUser_managedProfileIsNotIncluded() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
             userInfos.add(
@@ -889,7 +889,7 @@
         }
 
     @Test
-    fun `current user is not primary and user switcher is disabled`() =
+    fun currentUserIsNotPrimaryAndUserSwitcherIsDisabled() =
         testScope.runTest {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 9b74c1f..fd8c6c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -137,7 +137,7 @@
     }
 
     @Test
-    fun `config is false - chip is disabled`() {
+    fun configIsFalse_chipIsDisabled() {
         // the enabled bit is set at SystemUI startup, so recreate the view model here
         userRepository.isStatusBarUserChipEnabled = false
         underTest = viewModel()
@@ -146,7 +146,7 @@
     }
 
     @Test
-    fun `config is true - chip is enabled`() {
+    fun configIsTrue_chipIsEnabled() {
         // the enabled bit is set at SystemUI startup, so recreate the view model here
         userRepository.isStatusBarUserChipEnabled = true
         underTest = viewModel()
@@ -155,7 +155,7 @@
     }
 
     @Test
-    fun `should show chip criteria - single user`() =
+    fun shouldShowChipCriteria_singleUser() =
         testScope.runTest {
             userRepository.setUserInfos(listOf(USER_0))
             userRepository.setSelectedUserInfo(USER_0)
@@ -172,7 +172,7 @@
         }
 
     @Test
-    fun `should show chip criteria - multiple users`() =
+    fun shouldShowChipCriteria_multipleUsers() =
         testScope.runTest {
             setMultipleUsers()
 
@@ -186,7 +186,7 @@
         }
 
     @Test
-    fun `user chip name - shows selected user info`() =
+    fun userChipName_showsSelectedUserInfo() =
         testScope.runTest {
             setMultipleUsers()
 
@@ -206,7 +206,7 @@
         }
 
     @Test
-    fun `user chip avatar - shows selected user info`() =
+    fun userChipAvatar_showsSelectedUserInfo() =
         testScope.runTest {
             setMultipleUsers()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index a342dad..9155084 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -232,7 +232,7 @@
         }
 
     @Test
-    fun `maximumUserColumns - few users`() =
+    fun maximumUserColumns_fewUsers() =
         testScope.runTest {
             setUsers(count = 2)
             val values = mutableListOf<Int>()
@@ -244,7 +244,7 @@
         }
 
     @Test
-    fun `maximumUserColumns - many users`() =
+    fun maximumUserColumns_manyUsers() =
         testScope.runTest {
             setUsers(count = 5)
             val values = mutableListOf<Int>()
@@ -255,7 +255,7 @@
         }
 
     @Test
-    fun `isOpenMenuButtonVisible - has actions - true`() =
+    fun isOpenMenuButtonVisible_hasActions_true() =
         testScope.runTest {
             setUsers(2)
 
@@ -267,7 +267,7 @@
         }
 
     @Test
-    fun `isOpenMenuButtonVisible - no actions - false`() =
+    fun isOpenMenuButtonVisible_noActions_false() =
         testScope.runTest {
             val userInfos = setUsers(2)
             userRepository.setSelectedUserInfo(userInfos[1])
@@ -298,7 +298,7 @@
         }
 
     @Test
-    fun `menu actions`() =
+    fun menuActions() =
         testScope.runTest {
             setUsers(2)
             val actions = mutableListOf<List<UserActionViewModel>>()
@@ -318,7 +318,7 @@
         }
 
     @Test
-    fun `isFinishRequested - finishes when cancel button is clicked`() =
+    fun isFinishRequested_finishesWhenCancelButtonIsClicked() =
         testScope.runTest {
             setUsers(count = 2)
             val isFinishRequested = mutableListOf<Boolean>()
@@ -338,7 +338,7 @@
         }
 
     @Test
-    fun `guest selected -- name is exit guest`() =
+    fun guestSelected_nameIsExitGuest() =
         testScope.runTest {
             val userInfos =
                 listOf(
@@ -386,7 +386,7 @@
         }
 
     @Test
-    fun `guest not selected -- name is guest`() =
+    fun guestNotSelected_nameIsGuest() =
         testScope.runTest {
             val userInfos =
                 listOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index e33bfd7..e06b43a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -374,7 +374,7 @@
     @After
     public void teardown() {
         if (mDialog != null) {
-            mDialog.clearInternalHandleAfterTest();
+            mDialog.clearInternalHandlerAfterTest();
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
index 9bd3a79..3901d72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
@@ -93,7 +93,7 @@
     }
 
     @Test
-    fun `state - has wallet cards- callbacks called`() = runTest {
+    fun state_hasWalletCardsCallbacksCalled() = runTest {
         setUpWalletClient(listOf(CARD_1, CARD_2, PAYMENT_CARD))
         val controller = createWalletContextualSuggestionsController(backgroundScope)
         var latest1 = emptyList<WalletCard>()
@@ -115,7 +115,7 @@
     }
 
     @Test
-    fun `state - no wallet cards - set suggestion cards`() = runTest {
+    fun state_noWalletCards_setSuggestionCards() = runTest {
         setUpWalletClient(emptyList())
         val controller = createWalletContextualSuggestionsController(backgroundScope)
         val latest =
@@ -132,7 +132,7 @@
     }
 
     @Test
-    fun `state - has wallet cards - set and update suggestion cards`() = runTest {
+    fun state_hasWalletCards_setAndUpdateSuggestionCards() = runTest {
         setUpWalletClient(listOf(CARD_1, CARD_2, PAYMENT_CARD))
         val controller = createWalletContextualSuggestionsController(backgroundScope)
         val latest =
@@ -151,7 +151,7 @@
     }
 
     @Test
-    fun `state - wallet cards error`() = runTest {
+    fun state_walletCardsError() = runTest {
         setUpWalletClient(shouldFail = true)
         val controller = createWalletContextualSuggestionsController(backgroundScope)
         val latest =
@@ -167,7 +167,7 @@
     }
 
     @Test
-    fun `state - has wallet cards - received contextual cards - feature disabled`() = runTest {
+    fun state_hasWalletCards_receivedContextualCards_featureDisabled() = runTest {
         whenever(featureFlags.isEnabled(eq(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)))
             .thenReturn(false)
         setUpWalletClient(listOf(CARD_1, CARD_2, PAYMENT_CARD))
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 a42acd3..bc3a5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -24,7 +24,6 @@
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -85,7 +84,6 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.launcher3.icons.BubbleBadgeIconFactory;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -1227,8 +1225,7 @@
                 mBubbleController,
                 mBubbleController.getStackView(),
                 new BubbleIconFactory(mContext,
-                        mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size)),
-                new BubbleBadgeIconFactory(mContext,
+                        mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size),
                         mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
                         mContext.getResources().getColor(R.color.important_conversation),
                         mContext.getResources().getDimensionPixelSize(
@@ -1736,13 +1733,13 @@
     @Test
     public void testShowOrHideAppBubble_addsAndExpand() {
         assertThat(mBubbleController.isStackExpanded()).isFalse();
-        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
 
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
 
         verify(mBubbleController).inflateAndAdd(any(Bubble.class), /* suppressFlyout= */ eq(true),
                 /* showInShade= */ eq(false));
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+                Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
         assertThat(mBubbleController.isStackExpanded()).isTrue();
     }
 
@@ -1756,7 +1753,8 @@
         // Calling this while collapsed will expand the app bubble
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
 
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+                Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
         assertThat(mBubbleController.isStackExpanded()).isTrue();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
     }
@@ -1764,13 +1762,15 @@
     @Test
     public void testShowOrHideAppBubble_collapseIfSelected() {
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+                Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
         assertThat(mBubbleController.isStackExpanded()).isTrue();
 
         // Calling this while the app bubble is expanded should collapse the stack
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
 
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+                Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
         assertThat(mBubbleController.isStackExpanded()).isFalse();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
         assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(mUser0);
@@ -1779,8 +1779,9 @@
     @Test
     public void testShowOrHideAppBubbleWithNonPrimaryUser_bubbleCollapsedWithExpectedUser() {
         UserHandle user10 = createUserHandle(/* userId = */ 10);
+        String appBubbleKey = Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), user10);
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
         assertThat(mBubbleController.isStackExpanded()).isTrue();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
         assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(user10);
@@ -1788,13 +1789,28 @@
         // Calling this while the app bubble is expanded should collapse the stack
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
 
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
         assertThat(mBubbleController.isStackExpanded()).isFalse();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
         assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(user10);
     }
 
     @Test
+    public void testShowOrHideAppBubbleOnUser10AndThenUser0_user0BubbleExpanded() {
+        UserHandle user10 = createUserHandle(/* userId = */ 10);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
+
+        String appBubbleUser0Key = Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleUser0Key);
+        assertThat(mBubbleController.isStackExpanded()).isTrue();
+        assertThat(mBubbleData.getBubbles()).hasSize(2);
+        assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(mUser0);
+        assertThat(mBubbleData.getBubbles().get(1).getUser()).isEqualTo(user10);
+    }
+
+    @Test
     public void testShowOrHideAppBubble_selectIfNotSelected() {
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
         mBubbleController.updateBubble(mBubbleEntry);
@@ -1803,7 +1819,8 @@
         assertThat(mBubbleController.isStackExpanded()).isTrue();
 
         mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
-        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+                Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
         assertThat(mBubbleController.isStackExpanded()).isTrue();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
new file mode 100644
index 0000000..8444c7b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.accessibility.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeAccessibilityRepository(
+    override val isTouchExplorationEnabled: MutableStateFlow<Boolean> = MutableStateFlow(false)
+) : AccessibilityRepository
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
index 3041888..843cc3b 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
@@ -34,6 +34,7 @@
         }
 
     private var inProgress = false
+    private var receivedProgressEvent = false
 
     private var processedProgress: Float = 1.0f
         set(newProgress) {
@@ -54,7 +55,16 @@
     override fun onTransitionProgress(progress: Float) {
         logCounter({ "$TAG#plain_remote_progress" }, progress)
         if (inProgress) {
-            springAnimation.animateToFinalPosition(progress)
+            if (receivedProgressEvent) {
+                // We have received at least one progress event, animate from the previous
+                // progress to the current
+                springAnimation.animateToFinalPosition(progress)
+            } else {
+                // This is the first progress event after starting the animation, send it
+                // straightaway and set the spring value without animating it
+                processedProgress = progress
+                receivedProgressEvent = true
+            }
         } else {
             Log.e(TAG, "Progress received while not in progress.")
         }
@@ -62,6 +72,7 @@
 
     override fun onTransitionFinished() {
         inProgress = false
+        receivedProgressEvent = false
         listener.onTransitionFinished()
     }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 46001a7..8c5244e 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -122,8 +122,8 @@
                     "lastHingeAngle: $lastHingeAngle, " +
                     "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition"
             )
-            Trace.setCounter("hinge_angle", angle.toLong())
         }
+        Trace.setCounter("DeviceFoldStateProvider#onHingeAngle", angle.toLong())
 
         val currentDirection =
                 if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 22e742b..c1c47f5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -231,6 +231,12 @@
      */
     public void transitionMagnificationModeLocked(int displayId, int targetMode,
             @NonNull TransitionCallBack transitionCallBack) {
+        // check if target mode is already activated
+        if (isActivated(displayId, targetMode)) {
+            transitionCallBack.onResult(displayId, true);
+            return;
+        }
+
         final PointF currentCenter = getCurrentMagnificationCenterLocked(displayId, targetMode);
         final DisableMagnificationCallback animationCallback =
                 getDisableMagnificationEndRunnableLocked(displayId);
@@ -322,13 +328,16 @@
                     : config.getScale();
             try {
                 setTransitionState(displayId, targetMode);
+                final MagnificationAnimationCallback magnificationAnimationCallback = animate
+                        ? success -> mAms.changeMagnificationMode(displayId, targetMode)
+                        : null;
                 // Activate or deactivate target mode depending on config activated value
                 if (targetMode == MAGNIFICATION_MODE_WINDOW) {
                     screenMagnificationController.reset(displayId, false);
                     if (targetActivated) {
                         windowMagnificationMgr.enableWindowMagnification(displayId,
                                 targetScale, magnificationCenter.x, magnificationCenter.y,
-                                animate ? STUB_ANIMATION_CALLBACK : null, id);
+                                magnificationAnimationCallback, id);
                     } else {
                         windowMagnificationMgr.disableWindowMagnification(displayId, false);
                     }
@@ -339,8 +348,8 @@
                             screenMagnificationController.register(displayId);
                         }
                         screenMagnificationController.setScaleAndCenter(displayId, targetScale,
-                                magnificationCenter.x, magnificationCenter.y, animate,
-                                id);
+                                magnificationCenter.x, magnificationCenter.y,
+                                magnificationAnimationCallback, id);
                     } else {
                         if (screenMagnificationController.isRegistered(displayId)) {
                             screenMagnificationController.reset(displayId, false);
@@ -348,6 +357,9 @@
                     }
                 }
             } finally {
+                if (!animate) {
+                    mAms.changeMagnificationMode(displayId, targetMode);
+                }
                 // Reset transition state after enabling target mode.
                 setTransitionState(displayId, null);
             }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2a964b8..3bd4547 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -56,6 +56,10 @@
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_FAILURE;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_SUCCESS;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_DATASET_AUTHENTICATION;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_FULL_AUTHENTICATION;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
@@ -75,6 +79,12 @@
 import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE;
 import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_TRIGGER_ID_SET;
 import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_UNKNOWN;
+import static com.android.server.autofill.SessionCommittedEventLogger.CommitReason;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_ACTIVITY_FINISHED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_VIEW_CHANGED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_VIEW_CLICKED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_VIEW_COMMITTED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_SESSION_DESTROYED;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
@@ -364,6 +374,11 @@
     private final long mStartTime;
 
     /**
+     * Count of FillRequests in the session.
+     */
+    private int mRequestCount;
+
+    /**
      * Starting timestamp of latency logger.
      * This is set when Session created or when the view is reset.
      */
@@ -1132,6 +1147,7 @@
             int flags) {
         final FillResponse existingResponse = viewState.getResponse();
         mFillRequestEventLogger.startLogForNewRequest();
+        mRequestCount++;
         mFillRequestEventLogger.maybeSetAppPackageUid(uid);
         mFillRequestEventLogger.maybeSetFlags(mFlags);
         if(mPreviouslyFillDialogPotentiallyStarted) {
@@ -1330,8 +1346,6 @@
         this.userId = userId;
         this.taskId = taskId;
         this.uid = uid;
-        mStartTime = SystemClock.elapsedRealtime();
-        mLatencyBaseTime = mStartTime;
         mService = service;
         mLock = lock;
         mUi = ui;
@@ -1347,11 +1361,17 @@
         mComponentName = componentName;
         mCompatMode = compatMode;
         mSessionState = STATE_ACTIVE;
+        // Initiate all loggers & counters.
+        mStartTime = SystemClock.elapsedRealtime();
+        mLatencyBaseTime = mStartTime;
+        mRequestCount = 0;
         mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
         mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
         mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
         mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
+        mSessionCommittedEventLogger.maybeSetComponentPackageUid(uid);
         mSaveEventLogger = SaveEventLogger.forSessionId(sessionId);
+
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
             mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
@@ -1971,6 +1991,7 @@
 
         // Start a new FillRequest logger for client suggestion fallback.
         mFillRequestEventLogger.startLogForNewRequest();
+        mRequestCount++;
         mFillRequestEventLogger.maybeSetAppPackageUid(uid);
         mFillRequestEventLogger.maybeSetFlags(
             flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
@@ -2187,6 +2208,8 @@
         }
         final Intent fillInIntent;
         synchronized (mLock) {
+            mPresentationStatsEventLogger.maybeSetAuthenticationType(
+                AUTHENTICATION_TYPE_FULL_AUTHENTICATION);
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#authenticate() rejected - session: "
                         + id + " destroyed");
@@ -2231,7 +2254,6 @@
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#save() rejected - session: "
                         + id + " destroyed");
-                mSaveEventLogger.logAndEndEvent();
                 return;
             }
         }
@@ -2251,7 +2273,6 @@
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
                         + id + " destroyed");
-                mSaveEventLogger.logAndEndEvent();
                 return;
             }
         }
@@ -2438,18 +2459,26 @@
         final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
         if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
             setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
+            // Augmented autofill is not logged.
+            mPresentationStatsEventLogger.logAndEndEvent();
             return;
         }
         if (mResponses == null) {
             // Typically happens when app explicitly called cancel() while the service was showing
             // the auth UI.
             Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
+            mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                AUTHENTICATION_RESULT_FAILURE);
+            mPresentationStatsEventLogger.logAndEndEvent();
             removeFromService();
             return;
         }
         final FillResponse authenticatedResponse = mResponses.get(requestId);
         if (authenticatedResponse == null || data == null) {
             Slog.w(TAG, "no authenticated response");
+            mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                AUTHENTICATION_RESULT_FAILURE);
+            mPresentationStatsEventLogger.logAndEndEvent();
             removeFromService();
             return;
         }
@@ -2461,6 +2490,9 @@
             final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx);
             if (dataset == null) {
                 Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
+                mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                    AUTHENTICATION_RESULT_FAILURE);
+                mPresentationStatsEventLogger.logAndEndEvent();
                 removeFromService();
                 return;
             }
@@ -2477,11 +2509,15 @@
         }
         if (result instanceof FillResponse) {
             logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
+            mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                AUTHENTICATION_RESULT_SUCCESS);
             replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
         } else if (result instanceof Dataset) {
             if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
                 logAuthenticationStatusLocked(requestId,
                         MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
+                mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                    AUTHENTICATION_RESULT_SUCCESS);
                 if (newClientState != null) {
                     if (sDebug) Slog.d(TAG,  "Updating client state from auth dataset");
                     mClientState = newClientState;
@@ -2497,6 +2533,8 @@
                         + authenticationId);
                 logAuthenticationStatusLocked(requestId,
                         MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
+                mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                    AUTHENTICATION_RESULT_FAILURE);
             }
         } else {
             if (result != null) {
@@ -2504,6 +2542,8 @@
             }
             logAuthenticationStatusLocked(requestId,
                     MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
+            mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+                AUTHENTICATION_RESULT_FAILURE);
             processNullResponseLocked(requestId, 0);
         }
     }
@@ -4790,6 +4830,7 @@
         }
         // Log FillRequest for Augmented Autofill.
         mFillRequestEventLogger.startLogForNewRequest();
+        mRequestCount++;
         mFillRequestEventLogger.maybeSetAppPackageUid(uid);
         mFillRequestEventLogger.maybeSetFlags(mFlags);
         mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID);
@@ -5036,6 +5077,7 @@
             if (dataset.getAuthentication() == null) {
                 if (generateEvent) {
                     mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType);
+                    mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex);
                 }
                 if (mCurrentViewId != null) {
                     mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -5046,6 +5088,8 @@
 
             // ...or handle authentication.
             mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
+            mPresentationStatsEventLogger.maybeSetAuthenticationType(
+                AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
             setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
             final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
             if (fillInIntent == null) {
@@ -5639,6 +5683,17 @@
      */
     @GuardedBy("mLock")
     RemoteFillService destroyLocked() {
+        // Log unlogged events.
+        mSessionCommittedEventLogger.maybeSetCommitReason(COMMIT_REASON_SESSION_DESTROYED);
+        mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+        mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
+            SystemClock.elapsedRealtime() - mStartTime);
+        mSessionCommittedEventLogger.logAndEndEvent();
+        mPresentationStatsEventLogger.logAndEndEvent();
+        mSaveEventLogger.logAndEndEvent();
+        mFillResponseEventLogger.logAndEndEvent();
+        mFillRequestEventLogger.logAndEndEvent();
+
         if (mDestroyed) {
             return null;
         }
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 92d72ac..541ec80 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_ACTIVITY_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_SESSION_DESTROYED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CHANGED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CLICKED;
@@ -53,7 +54,8 @@
       COMMIT_REASON_ACTIVITY_FINISHED,
       COMMIT_REASON_VIEW_COMMITTED,
       COMMIT_REASON_VIEW_CLICKED,
-      COMMIT_REASON_VIEW_CHANGED
+      COMMIT_REASON_VIEW_CHANGED,
+      COMMIT_REASON_SESSION_DESTROYED
   })
   @Retention(RetentionPolicy.SOURCE)
   public @interface CommitReason {
@@ -69,6 +71,8 @@
       AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CLICKED;
   public static final int COMMIT_REASON_VIEW_CHANGED =
       AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CHANGED;
+  public static final int COMMIT_REASON_SESSION_DESTROYED =
+      AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_SESSION_DESTROYED;
 
   private final int mSessionId;
   private Optional<SessionCommittedEventInternal> mEventInternal;
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 756dcd2..b68adab 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -361,6 +361,7 @@
         params.width = WindowManager.LayoutParams.MATCH_PARENT;
         params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title);
         params.windowAnimations = R.style.AutofillSaveAnimation;
+        params.setTrustedOverlay();
 
         show();
     }
diff --git a/services/backup/BACKUP_OWNERS b/services/backup/BACKUP_OWNERS
new file mode 100644
index 0000000..f8f4f4f
--- /dev/null
+++ b/services/backup/BACKUP_OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 1193469
+
+jstemmer@google.com
+martinoh@google.com
+millmore@google.com
+niamhfw@google.com
+piee@google.com
+philippov@google.com
+rthakohov@google.com
+sarpm@google.com
\ No newline at end of file
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 79709a3..3bd2db1 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -2,12 +2,4 @@
 
 set noparent
 
-bryanmawhinney@google.com
-jstemmer@google.com
-martinoh@google.com
-millmore@google.com
-niamhfw@google.com
-piee@google.com
-philippov@google.com
-rthakohov@google.com
-sarpm@google.com
+include platform/frameworks/base:/services/backup/BACKUP_OWNERS
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index d2c41a4..efff112 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -900,7 +900,7 @@
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
             new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, mAssociationStore,
-                    mDevicePresenceMonitor, mTransportManager)
+                    mDevicePresenceMonitor, mTransportManager, mSystemDataTransferRequestStore)
                     .exec(this, in, out, err, args, callback, resultReceiver);
         }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 6de3585..669686ad 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -17,10 +17,12 @@
 package com.android.server.companion;
 
 import android.companion.AssociationInfo;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.net.MacAddress;
 import android.os.Binder;
 import android.os.ShellCommand;
 
+import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
 import com.android.server.companion.transport.CompanionTransportManager;
 
@@ -35,14 +37,18 @@
     private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
     private final CompanionTransportManager mTransportManager;
 
+    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
+
     CompanionDeviceShellCommand(CompanionDeviceManagerService service,
             AssociationStore associationStore,
             CompanionDevicePresenceMonitor devicePresenceMonitor,
-            CompanionTransportManager transportManager) {
+            CompanionTransportManager transportManager,
+            SystemDataTransferRequestStore systemDataTransferRequestStore) {
         mService = service;
         mAssociationStore = associationStore;
         mDevicePresenceMonitor = devicePresenceMonitor;
         mTransportManager = transportManager;
+        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
     }
 
     @Override
@@ -59,7 +65,7 @@
                         // TODO(b/212535524): use AssociationInfo.toShortString(), once it's not
                         //  longer referenced in tests.
                         out.println(association.getPackageName() + " "
-                                + association.getDeviceMacAddress());
+                                + association.getDeviceMacAddress() + " " + association.getId());
                     }
                 }
                 break;
@@ -117,6 +123,17 @@
                     mTransportManager.createDummyTransport(associationId);
                     break;
 
+                case "allow-permission-sync": {
+                    int userId = getNextIntArgRequired();
+                    associationId = getNextIntArgRequired();
+                    boolean enabled = getNextBooleanArgRequired();
+                    PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+                    request.setUserId(userId);
+                    request.setUserConsented(enabled);
+                    mSystemDataTransferRequestStore.writeRequest(userId, request);
+                }
+                break;
+
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -134,6 +151,15 @@
         return Integer.parseInt(getNextArgRequired());
     }
 
+    private boolean getNextBooleanArgRequired() {
+        String arg = getNextArgRequired();
+        if ("true".equalsIgnoreCase(arg) || "false".equalsIgnoreCase(arg)) {
+            return Boolean.parseBoolean(arg);
+        } else {
+            throw new IllegalArgumentException("Expected a boolean argument but was: " + arg);
+        }
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 2c5d582..720cefa 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -120,12 +120,14 @@
         return requestsByAssociationId;
     }
 
-    void writeRequest(@UserIdInt int userId, SystemDataTransferRequest request) {
+    public void writeRequest(@UserIdInt int userId, SystemDataTransferRequest request) {
         Slog.i(LOG_TAG, "Writing request=" + request + " to store.");
         ArrayList<SystemDataTransferRequest> cachedRequests;
         synchronized (mLock) {
             // Write to cache
             cachedRequests = readRequestsFromCache(userId);
+            cachedRequests.removeIf(
+                    request1 -> request1.getAssociationId() == request.getAssociationId());
             cachedRequests.add(request);
             mCachedPerUser.set(userId, cachedRequests);
         }
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index a8519e3..0457e9a 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -28,7 +28,6 @@
 import com.google.security.cryptauth.lib.securegcm.D2DConnectionContextV1;
 import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext;
 import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext.Role;
-import com.google.security.cryptauth.lib.securegcm.DefaultUkey2Logger;
 import com.google.security.cryptauth.lib.securegcm.HandshakeException;
 
 import libcore.io.IoUtils;
@@ -329,7 +328,7 @@
         }
 
         mRole = Role.Initiator;
-        mHandshakeContext = D2DHandshakeContext.forInitiator(DefaultUkey2Logger.INSTANCE);
+        mHandshakeContext = D2DHandshakeContext.forInitiator();
 
         // Send Client Init
         if (DEBUG) {
@@ -350,7 +349,7 @@
 
         if (mHandshakeContext == null) { // Server-side logic
             mRole = Role.Responder;
-            mHandshakeContext = D2DHandshakeContext.forResponder(DefaultUkey2Logger.INSTANCE);
+            mHandshakeContext = D2DHandshakeContext.forResponder();
 
             // Receive Client Init
             if (DEBUG) {
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 4b76127..7fae31c 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -338,6 +338,9 @@
     // marked as stopped by the system
     @NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>();
 
+    // A map of preloaded package names and the path to its app metadata file path.
+    private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>();
+
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
@@ -536,6 +539,10 @@
         return mInitialNonStoppedSystemPackages;
     }
 
+    public ArrayMap<String, String> getAppMetadataFilePaths() {
+        return mAppMetadataFilePaths;
+    }
+
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1466,7 +1473,20 @@
                         } else if (!Boolean.parseBoolean(stopped)) {
                             mInitialNonStoppedSystemPackages.add(pkgName);
                         }
-                    }
+                    } break;
+                    case "asl-file": {
+                        String packageName = parser.getAttributeValue(null, "package");
+                        String path = parser.getAttributeValue(null, "path");
+                        if (TextUtils.isEmpty(packageName)) {
+                            Slog.w(TAG, "<" + name + "> without valid package in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else if (TextUtils.isEmpty(path)) {
+                            Slog.w(TAG, "<" + name + "> without valid path in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            mAppMetadataFilePaths.put(packageName, path);
+                        }
+                    } break;
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 51d349f..9f9642c 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5912,22 +5912,24 @@
     }
 
     private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
-        // the managing app can always modify accounts
-        if (isProfileOwner(callingUid)) {
-            return true;
-        }
-        DevicePolicyManager dpm = (DevicePolicyManager) mContext
-                .getSystemService(Context.DEVICE_POLICY_SERVICE);
-        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
-        if (typesArray == null) {
-            return true;
-        }
-        for (String forbiddenType : typesArray) {
-            if (forbiddenType.equals(accountType)) {
-                return false;
+        return Binder.withCleanCallingIdentity(() -> {
+            // the managing app can always modify accounts
+            if (isProfileOwner(callingUid)) {
+                return true;
             }
-        }
-        return true;
+            DevicePolicyManager dpm = (DevicePolicyManager) mContext
+                    .getSystemService(Context.DEVICE_POLICY_SERVICE);
+            String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
+            if (typesArray == null) {
+                return true;
+            }
+            for (String forbiddenType : typesArray) {
+                if (forbiddenType.equals(accountType)) {
+                    return false;
+                }
+            }
+            return true;
+        });
     }
 
     private boolean isProfileOwner(int uid) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6adccf6..df3c95b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -829,7 +829,8 @@
         // Service.startForeground()), at that point we will consult the BFSL check and the timeout
         // and make the necessary decisions.
         setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
-                backgroundStartPrivileges, false /* isBindService */);
+                backgroundStartPrivileges, false /* isBindService */,
+                !fgRequired /* isStartService */);
 
         if (!mAm.mUserController.exists(r.userId)) {
             Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
@@ -2119,7 +2120,11 @@
                         }
                     }
 
-                    if (r.isForeground && isOldTypeShortFgs) {
+                    final boolean enableFgsWhileInUseFix = mAm.mConstants.mEnableFgsWhileInUseFix;
+                    final boolean fgsTypeChangingFromShortFgs = r.isForeground && isOldTypeShortFgs;
+
+                    if (fgsTypeChangingFromShortFgs) {
+
                         // If we get here, that means startForeground(SHORT_SERVICE) is called again
                         // on a SHORT_SERVICE FGS.
 
@@ -2128,7 +2133,7 @@
                         setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
                                 r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                 BackgroundStartPrivileges.NONE,
-                                false /* isBindService */);
+                                false /* isBindService */, false /* isStartService */);
                         if (r.mAllowStartForeground == REASON_DENIED) {
                             Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
                                     + " BFSL DENIED.");
@@ -2171,8 +2176,19 @@
                                 // "if (r.mAllowStartForeground == REASON_DENIED...)" block below.
                             }
                         }
+                    }
 
-                    } else if (r.mStartForegroundCount == 0) {
+                    // Re-evaluate mAllowWhileInUsePermissionInFgs and mAllowStartForeground
+                    // (i.e. while-in-use and BFSL flags) if needed.
+                    //
+                    // Consider the below if-else section to be in the else of the above
+                    // `if (fgsTypeChangingFromShortFgs)`.
+                    // Using an else would increase the indent further, so we don't use it here
+                    // and instead just add !fgsTypeChangingFromShortFgs to all if's.
+                    //
+                    // The first if's are for the original while-in-use logic.
+                    if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
+                            && r.mStartForegroundCount == 0) {
                         /*
                         If the service was started with startService(), not
                         startForegroundService(), and if startForeground() isn't called within
@@ -2193,7 +2209,7 @@
                                 setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
                                         r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                         BackgroundStartPrivileges.NONE,
-                                        false /* isBindService */);
+                                        false /* isBindService */, false /* isStartService */);
                                 final String temp = "startForegroundDelayMs:" + delayMs;
                                 if (r.mInfoAllowStartForeground != null) {
                                     r.mInfoAllowStartForeground += "; " + temp;
@@ -2203,9 +2219,10 @@
                                 r.mLoggedInfoAllowStartForeground = false;
                             }
                         }
-                    } else if (r.mStartForegroundCount >= 1) {
+                    } else if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
+                            && r.mStartForegroundCount >= 1) {
                         // We get here if startForeground() is called multiple times
-                        // on the same sarvice after it's created, regardless of whether
+                        // on the same service after it's created, regardless of whether
                         // stopForeground() has been called or not.
 
                         // The second or later time startForeground() is called after service is
@@ -2213,7 +2230,71 @@
                         setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
                                 r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                 BackgroundStartPrivileges.NONE,
-                                false /* isBindService */);
+                                false /* isBindService */, false /* isStartService */);
+                    } else if (!fgsTypeChangingFromShortFgs && enableFgsWhileInUseFix) {
+                        // The new while-in-use logic.
+                        //
+                        // When startForeground() is called, we _always_ call
+                        // setFgsRestrictionLocked() to set the restrictions according to the
+                        // current state of the app.
+                        // (So if the app is now in TOP, for example, the service will now always
+                        // get while-in-use permissions.)
+                        //
+                        // Note, setFgsRestrictionLocked() will never disallow
+                        // mAllowWhileInUsePermissionInFgs nor mAllowStartForeground
+                        // (i.e. while-in-use and BFSL flags) once they're set to "allowed".
+                        //
+                        // HOWEVER, if these flags were set to "allowed" in Context.startService()
+                        // (as opposed to startForegroundService()), when the service wasn't yet
+                        // a foreground service, then we may not always
+                        // want to trust them -- for example, if the service has been running as a
+                        // BG service or a bound service for a long time when the app is not longer
+                        // in the foreground, then we shouldn't grant while-in-user nor BFSL.
+                        // So in that case, we need to reset it first.
+
+                        final long delayMs =
+                                (r.mLastUntrustedSetFgsRestrictionAllowedTime == 0) ? 0
+                                : (SystemClock.elapsedRealtime()
+                                    - r.mLastUntrustedSetFgsRestrictionAllowedTime);
+                        final boolean resetNeeded =
+                                !r.isForeground
+                                && delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs;
+                        if (resetNeeded) {
+                            resetFgsRestrictionLocked(r);
+                        }
+                        setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
+                                r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+                                BackgroundStartPrivileges.NONE,
+                                false /* isBindService */, false /* isStartService */);
+
+                        final String temp = "startForegroundDelayMs:" + delayMs
+                                + "; started: " + r.startRequested
+                                + "; num_bindings: " + r.getConnections().size()
+                                + "; wasForeground: " + r.isForeground
+                                + "; resetNeeded:" + resetNeeded;
+                        if (r.mInfoAllowStartForeground != null) {
+                            r.mInfoAllowStartForeground += "; " + temp;
+                        } else {
+                            r.mInfoAllowStartForeground = temp;
+                        }
+                        r.mLoggedInfoAllowStartForeground = false;
+                    }
+
+                    // If the service has any bindings and it's not yet a FGS
+                    // we compare the new and old while-in-use logics.
+                    // (If it's not the first startForeground() call, we already reset the
+                    // while-in-use and BFSL flags, so the logic change wouldn't matter.)
+                    if (enableFgsWhileInUseFix
+                            && !r.isForeground
+                            && (r.getConnections().size() > 0)
+                            && (r.mDebugWhileInUseReasonInBindService
+                            != r.mDebugWhileInUseReasonInStartForeground)) {
+                        Slog.wtf(TAG, "FGS while-in-use changed (b/276963716): old="
+                                + reasonCodeToString(r.mDebugWhileInUseReasonInBindService)
+                                + " new="
+                                + reasonCodeToString(r.mDebugWhileInUseReasonInStartForeground)
+                                + " "
+                                + r.shortInstanceName);
                     }
 
                     // If the foreground service is not started from TOP process, do not allow it to
@@ -2321,6 +2402,11 @@
                             active.mNumActive++;
                         }
                         r.isForeground = true;
+
+                        // Once the service becomes a foreground service,
+                        // the FGS restriction information always becomes "trustable".
+                        r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
+
                         // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
                         // be deferred, make a copy of mAllowStartForeground and
                         // mAllowWhileInUsePermissionInFgs.
@@ -3663,8 +3749,25 @@
                     return 0;
                 }
             }
-            setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
-                    BackgroundStartPrivileges.NONE, true /* isBindService */);
+            if (!mAm.mConstants.mEnableFgsWhileInUseFix) {
+                // Old while-in-use logic.
+                setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
+                        BackgroundStartPrivileges.NONE, true /* isBindService */,
+                        false /* isStartService */);
+            } else {
+                // New logic will not call setFgsRestrictionLocked() here, but we still
+                // keep track of the allow reason from the old logic here, so we can compare to
+                // the new logic.
+                // Once we're confident enough in the new logic, we should remove it.
+                if (s.mDebugWhileInUseReasonInBindService == REASON_DENIED) {
+                    s.mDebugWhileInUseReasonInBindService =
+                            shouldAllowFgsWhileInUsePermissionLocked(
+                            callingPackage, callingPid, callingUid, s.app,
+                            BackgroundStartPrivileges.NONE,
+                            true /* isBindService */,
+                            false /* DO NOT enableFgsWhileInUseFix; use the old logic */);
+                }
+            }
 
             if (s.app != null) {
                 ProcessServiceRecord servicePsr = s.app.mServices;
@@ -7357,45 +7460,76 @@
      * @param callingUid caller app's uid.
      * @param intent intent to start/bind service.
      * @param r the service to start.
+     * @param isStartService True if it's called from Context.startService().
+     *                       False if it's called from Context.startForegroundService() or
+     *                       Service.startService().
      * @return true if allow, false otherwise.
      */
     private void setFgsRestrictionLocked(String callingPackage,
             int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
-            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
-        r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
+            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+            boolean isStartService) {
+        final long now = SystemClock.elapsedRealtime();
+
         // Check DeviceConfig flag.
         if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
             r.mAllowWhileInUsePermissionInFgs = true;
         }
 
         final @ReasonCode int allowWhileInUse;
+
+        // Either (or both) mAllowWhileInUsePermissionInFgs or mAllowStartForeground is
+        // newly allowed?
+        boolean newlyAllowed = false;
         if (!r.mAllowWhileInUsePermissionInFgs
                 || (r.mAllowStartForeground == REASON_DENIED)) {
             allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                     callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges,
                     isBindService);
+            // We store them to compare the old and new while-in-use logics to each other.
+            // (They're not used for any other purposes.)
+            if (isBindService) {
+                r.mDebugWhileInUseReasonInBindService = allowWhileInUse;
+            } else {
+                r.mDebugWhileInUseReasonInStartForeground = allowWhileInUse;
+            }
             if (!r.mAllowWhileInUsePermissionInFgs) {
                 r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
+                newlyAllowed |= r.mAllowWhileInUsePermissionInFgs;
             }
             if (r.mAllowStartForeground == REASON_DENIED) {
                 r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
                         allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
                         backgroundStartPrivileges, isBindService);
+                newlyAllowed |= r.mAllowStartForeground != REASON_DENIED;
             }
         } else {
             allowWhileInUse = REASON_UNKNOWN;
         }
         r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
+
+        if (isStartService && !r.isForeground && newlyAllowed) {
+            // If it's called by Context.startService() (not by startForegroundService()),
+            // and we're setting "allowed", then we can't fully trust it yet -- we'll need to reset
+            // the restrictions if startForeground() is called after the grace period.
+            r.mLastUntrustedSetFgsRestrictionAllowedTime = now;
+        }
     }
 
     void resetFgsRestrictionLocked(ServiceRecord r) {
         r.mAllowWhileInUsePermissionInFgs = false;
         r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
+        r.mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
+        // We don't reset mWhileInUseReasonInBindService here, because if we do this, we would
+        // lose it in the "reevaluation" case in startForeground(), where we call
+        // resetFgsRestrictionLocked().
+        // Not resetting this is fine because it's only used in the first Service.startForeground()
+        // case, and there's no situations where we call resetFgsRestrictionLocked() before that.
         r.mAllowStartForeground = REASON_DENIED;
         r.mInfoAllowStartForeground = null;
         r.mInfoTempFgsAllowListReason = null;
         r.mLoggedInfoAllowStartForeground = false;
-        r.mLastSetFgsRestrictionTime = 0;
+        r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
         r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs);
     }
 
@@ -7430,14 +7564,29 @@
     private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
             int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
             BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+        return shouldAllowFgsWhileInUsePermissionLocked(callingPackage,
+                callingPid, callingUid, targetProcess, backgroundStartPrivileges, isBindService,
+                /* enableFgsWhileInUseFix =*/ mAm.mConstants.mEnableFgsWhileInUseFix);
+    }
+
+    private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
+            int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
+            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+            boolean enableFgsWhileInUseFix) {
         int ret = REASON_DENIED;
 
-        final int uidState = mAm.getUidStateLocked(callingUid);
-        if (ret == REASON_DENIED) {
-            // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
-            // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP.
-            if (uidState <= PROCESS_STATE_TOP) {
-                ret = getReasonCodeFromProcState(uidState);
+        // Define some local variables for better readability...
+        final boolean useOldLogic = !enableFgsWhileInUseFix;
+        final boolean forStartForeground = !isBindService;
+
+        if (useOldLogic || forStartForeground) {
+            final int uidState = mAm.getUidStateLocked(callingUid);
+            if (ret == REASON_DENIED) {
+                // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
+                // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP.
+                if (uidState <= PROCESS_STATE_TOP) {
+                    ret = getReasonCodeFromProcState(uidState);
+                }
             }
         }
 
@@ -7480,6 +7629,10 @@
             }
         }
 
+        if (enableFgsWhileInUseFix && ret == REASON_DENIED) {
+            ret = shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
+        }
+
         if (ret == REASON_DENIED) {
             // Allow FGS while-in-use if the WindowManager allows background activity start.
             // This is mainly to get the 10 seconds grace period if any activity in the caller has
@@ -7558,6 +7711,59 @@
     }
 
     /**
+     * Check all bindings into the calling UID, and see if:
+     * - It's bound by a TOP app
+     * - or, bound by a persistent process with BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS.
+     */
+    private @ReasonCode int shouldAllowFgsWhileInUsePermissionByBindingsLocked(int callingUid) {
+        final ArraySet<Integer> checkedClientUids = new ArraySet<>();
+        final Integer result = mAm.mProcessList.searchEachLruProcessesLOSP(
+                false, pr -> {
+                    if (pr.uid != callingUid) {
+                        return null;
+                    }
+                    final ProcessServiceRecord psr = pr.mServices;
+                    final int serviceCount = psr.mServices.size();
+                    for (int svc = 0; svc < serviceCount; svc++) {
+                        final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
+                                psr.mServices.valueAt(svc).getConnections();
+                        final int size = conns.size();
+                        for (int conni = 0; conni < size; conni++) {
+                            final ArrayList<ConnectionRecord> crs = conns.valueAt(conni);
+                            for (int con = 0; con < crs.size(); con++) {
+                                final ConnectionRecord cr = crs.get(con);
+                                final ProcessRecord clientPr = cr.binding.client;
+                                final int clientUid = clientPr.uid;
+
+                                // An UID can bind to itself, do not check on itself again.
+                                // Also skip already checked clientUid.
+                                if (clientUid == callingUid
+                                        || checkedClientUids.contains(clientUid)) {
+                                    continue;
+                                }
+
+                                // Binding found, check the client procstate and the flag.
+                                final int clientUidState = mAm.getUidStateLocked(callingUid);
+                                final boolean boundByTop = clientUidState == PROCESS_STATE_TOP;
+                                final boolean boundByPersistentWithBal =
+                                        clientUidState < PROCESS_STATE_TOP
+                                        && cr.hasFlag(
+                                                Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS);
+                                if (boundByTop || boundByPersistentWithBal) {
+                                    return getReasonCodeFromProcState(clientUidState);
+                                }
+
+                                // Don't check the same UID.
+                                checkedClientUids.add(clientUid);
+                            }
+                        }
+                    }
+                    return null;
+                });
+        return result == null ? REASON_DENIED : result;
+    }
+
+    /**
      * The uid is not allowed to start FGS, but the uid has a service that is bound
      * by a clientUid, if the clientUid can start FGS, then the clientUid can propagate its
      * BG-FGS-start capability down to the callingUid.
@@ -8142,7 +8348,8 @@
         r.mFgsEnterTime = SystemClock.uptimeMillis();
         r.foregroundServiceType = options.mForegroundServiceTypes;
         setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
-                BackgroundStartPrivileges.NONE, false);
+                BackgroundStartPrivileges.NONE,  false /* isBindService */,
+                false /* isStartService */);
         final ProcessServiceRecord psr = callerApp.mServices;
         final boolean newService = psr.startService(r);
         // updateOomAdj.
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 44e198b..3841b6a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1058,6 +1058,13 @@
     /** @see #KEY_USE_MODERN_TRIM */
     public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM;
 
+    private static final String KEY_ENABLE_FGS_WHILE_IN_USE_FIX =
+            "key_enable_fgs_while_in_use_fix";
+
+    private static final boolean DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX = true;
+
+    public volatile boolean mEnableFgsWhileInUseFix = DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX;
+
     private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
             new OnPropertiesChangedListener() {
                 @Override
@@ -1226,6 +1233,9 @@
                             case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION:
                                 updateEnableWaitForFinishAttachApplication();
                                 break;
+                            case KEY_ENABLE_FGS_WHILE_IN_USE_FIX:
+                                updateEnableFgsWhileInUseFix();
+                                break;
                             case KEY_MAX_PREVIOUS_TIME:
                                 updateMaxPreviousTime();
                                 break;
@@ -1995,6 +2005,12 @@
                 DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
     }
 
+    private void updateEnableFgsWhileInUseFix() {
+        mEnableFgsWhileInUseFix = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_ENABLE_FGS_WHILE_IN_USE_FIX,
+                DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX);
+    }
     private void updateUseTieredCachedAdj() {
         USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean(
             DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2195,6 +2211,9 @@
         pw.print("  "); pw.print(KEY_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
         pw.print("="); pw.println(mFlagSystemExemptPowerRestrictionsEnabled);
 
+        pw.print("  "); pw.print(KEY_ENABLE_FGS_WHILE_IN_USE_FIX);
+        pw.print("="); pw.println(mEnableFgsWhileInUseFix);
+
         pw.print("  "); pw.print(KEY_SHORT_FGS_TIMEOUT_DURATION);
         pw.print("="); pw.println(mShortFgsTimeoutDuration);
         pw.print("  "); pw.print(KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 97d34b8..81dc346 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -382,6 +382,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StatsEvent;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
@@ -1595,6 +1596,7 @@
     static final int SERVICE_SHORT_FGS_TIMEOUT_MSG = 76;
     static final int SERVICE_SHORT_FGS_PROCSTATE_TIMEOUT_MSG = 77;
     static final int SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG = 78;
+    static final int UPDATE_CACHED_APP_HIGH_WATERMARK = 79;
 
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
 
@@ -1938,6 +1940,9 @@
                 case SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG: {
                     mServices.onShortFgsAnrTimeout((ServiceRecord) msg.obj);
                 } break;
+                case UPDATE_CACHED_APP_HIGH_WATERMARK: {
+                    mAppProfiler.mCachedAppsWatermarkData.updateCachedAppsSnapshot((long) msg.obj);
+                } break;
             }
         }
     }
@@ -7311,7 +7316,14 @@
             // Send broadcast to shell to trigger bugreport using Bugreport API
             // Always start the shell process on the current user to ensure that
             // the foreground user can see all bugreport notifications.
-            mContext.sendBroadcastAsUser(triggerShellBugreport, getCurrentUser().getUserHandle());
+            // In case of BUGREPORT_MODE_REMOTE send the broadcast to SYSTEM user as the device
+            // owner apps are running on the SYSTEM user.
+            if (bugreportType == BugreportParams.BUGREPORT_MODE_REMOTE) {
+                mContext.sendBroadcastAsUser(triggerShellBugreport, UserHandle.SYSTEM);
+            } else {
+                mContext.sendBroadcastAsUser(triggerShellBugreport,
+                        getCurrentUser().getUserHandle());
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -18598,6 +18610,13 @@
                 @MediaProjectionTokenEvent int event) {
             ActivityManagerService.this.notifyMediaProjectionEvent(uid, projectionToken, event);
         }
+
+        @Override
+        @NonNull
+        public StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull) {
+            return mAppProfiler.mCachedAppsWatermarkData.getCachedAppsHighWatermarkStats(
+                    atomTag, resetAfterPull);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 16219cd..7d98443 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -22,6 +22,7 @@
 import android.content.pm.ApplicationInfo;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,8 +30,12 @@
 import com.android.internal.os.TimeoutRecord;
 import com.android.server.wm.WindowProcessController;
 
+import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -59,13 +64,19 @@
     /**
      * The keep alive time for the threads in the helper threadpool executor
     */
-    private static final int AUX_THREAD_KEEP_ALIVE_SECOND = 10;
+    private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10;
 
     private static final ThreadFactory sDefaultThreadFactory =  r ->
             new Thread(r, "AnrAuxiliaryTaskExecutor");
+    private static final ThreadFactory sMainProcessDumpThreadFactory =  r ->
+            new Thread(r, "AnrMainProcessDumpThread");
 
     @GuardedBy("mAnrRecords")
     private final ArrayList<AnrRecord> mAnrRecords = new ArrayList<>();
+
+    private final Set<Integer> mTempDumpedPids =
+            Collections.synchronizedSet(new ArraySet<Integer>());
+
     private final AtomicBoolean mRunning = new AtomicBoolean(false);
 
     private final ActivityManagerService mService;
@@ -80,17 +91,21 @@
     private int mProcessingPid = -1;
 
     private final ExecutorService mAuxiliaryTaskExecutor;
+    private final ExecutorService mEarlyDumpExecutor;
 
     AnrHelper(final ActivityManagerService service) {
-        this(service, new ThreadPoolExecutor(/* corePoolSize= */ 0, /* maximumPoolSize= */ 1,
-                /* keepAliveTime= */ AUX_THREAD_KEEP_ALIVE_SECOND, TimeUnit.SECONDS,
-                new LinkedBlockingQueue<>(), sDefaultThreadFactory));
+        // All the ANR threads need to expire after a period of inactivity, given the
+        // ephemeral nature of ANRs and how infrequent they are.
+        this(service, makeExpiringThreadPoolWithSize(1, sDefaultThreadFactory),
+                makeExpiringThreadPoolWithSize(2, sMainProcessDumpThreadFactory));
     }
 
     @VisibleForTesting
-    AnrHelper(ActivityManagerService service, ExecutorService auxExecutor) {
+    AnrHelper(ActivityManagerService service, ExecutorService auxExecutor,
+            ExecutorService earlyDumpExecutor) {
         mService = service;
         mAuxiliaryTaskExecutor = auxExecutor;
+        mEarlyDumpExecutor = earlyDumpExecutor;
     }
 
     void appNotResponding(ProcessRecord anrProcess, TimeoutRecord timeoutRecord) {
@@ -121,6 +136,12 @@
                             + timeoutRecord.mReason);
                     return;
                 }
+                if (!mTempDumpedPids.add(incomingPid)) {
+                    Slog.i(TAG,
+                            "Skip ANR being predumped, pid=" + incomingPid + " "
+                            + timeoutRecord.mReason);
+                    return;
+                }
                 for (int i = mAnrRecords.size() - 1; i >= 0; i--) {
                     if (mAnrRecords.get(i).mPid == incomingPid) {
                         Slog.i(TAG,
@@ -129,10 +150,24 @@
                         return;
                     }
                 }
+                // We dump the main process as soon as we can on a different thread,
+                // this is done as the main process's dump can go stale in a few hundred
+                // milliseconds and the average full ANR dump takes a few seconds.
+                timeoutRecord.mLatencyTracker.earlyDumpRequestSubmittedWithSize(
+                        mTempDumpedPids.size());
+                Future<File> firstPidDumpPromise = mEarlyDumpExecutor.submit(() -> {
+                    // the class AnrLatencyTracker is not generally thread safe but the values
+                    // recorded/touched by the Temporary dump thread(s) are all volatile/atomic.
+                    File tracesFile = StackTracesDumpHelper.dumpStackTracesTempFile(incomingPid,
+                            timeoutRecord.mLatencyTracker);
+                    mTempDumpedPids.remove(incomingPid);
+                    return tracesFile;
+                });
+
                 timeoutRecord.mLatencyTracker.anrRecordPlacingOnQueueWithSize(mAnrRecords.size());
                 mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
-                        parentShortComponentName, parentProcess, aboveSystem,
-                        mAuxiliaryTaskExecutor, timeoutRecord, isContinuousAnr));
+                        parentShortComponentName, parentProcess, aboveSystem, timeoutRecord,
+                        isContinuousAnr, firstPidDumpPromise));
             }
             startAnrConsumerIfNeeded();
         } finally {
@@ -147,6 +182,16 @@
         }
     }
 
+    private static ThreadPoolExecutor makeExpiringThreadPoolWithSize(int size,
+            ThreadFactory factory) {
+        ThreadPoolExecutor pool = new ThreadPoolExecutor(/* corePoolSize= */ size,
+                /* maximumPoolSize= */ size, /* keepAliveTime= */ DEFAULT_THREAD_KEEP_ALIVE_SECOND,
+                TimeUnit.SECONDS, new LinkedBlockingQueue<>(), factory);
+        // We allow the core threads to expire after the keepAliveTime.
+        pool.allowCoreThreadTimeOut(true);
+        return pool;
+    }
+
     /**
      * The thread to execute {@link ProcessErrorStateRecord#appNotResponding}. It will terminate if
      * all records are handled.
@@ -219,7 +264,7 @@
         }
     }
 
-    private static class AnrRecord {
+    private class AnrRecord {
         final ProcessRecord mApp;
         final int mPid;
         final String mActivityShortComponentName;
@@ -228,14 +273,14 @@
         final ApplicationInfo mAppInfo;
         final WindowProcessController mParentProcess;
         final boolean mAboveSystem;
-        final ExecutorService mAuxiliaryTaskExecutor;
         final long mTimestamp = SystemClock.uptimeMillis();
         final boolean mIsContinuousAnr;
+        final Future<File> mFirstPidFilePromise;
         AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
                 ApplicationInfo aInfo, String parentShortComponentName,
                 WindowProcessController parentProcess, boolean aboveSystem,
-                ExecutorService auxiliaryTaskExecutor, TimeoutRecord timeoutRecord,
-                boolean isContinuousAnr) {
+                TimeoutRecord timeoutRecord, boolean isContinuousAnr,
+                Future<File> firstPidFilePromise) {
             mApp = anrProcess;
             mPid = anrProcess.mPid;
             mActivityShortComponentName = activityShortComponentName;
@@ -244,8 +289,8 @@
             mAppInfo = aInfo;
             mParentProcess = parentProcess;
             mAboveSystem = aboveSystem;
-            mAuxiliaryTaskExecutor = auxiliaryTaskExecutor;
             mIsContinuousAnr = isContinuousAnr;
+            mFirstPidFilePromise = firstPidFilePromise;
         }
 
         void appNotResponding(boolean onlyDumpSelf) {
@@ -254,7 +299,7 @@
                 mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
                         mParentShortComponentName, mParentProcess, mAboveSystem,
                         mTimeoutRecord, mAuxiliaryTaskExecutor, onlyDumpSelf,
-                        mIsContinuousAnr);
+                        mIsContinuousAnr, mFirstPidFilePromise);
             } finally {
                 mTimeoutRecord.mLatencyTracker.anrProcessingEnded();
             }
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 25ac956..f29a2e1 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -82,17 +82,21 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.StatsEvent;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BinderInternal;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.MemInfoReader;
+import com.android.internal.util.QuickSelect;
 import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.utils.PriorityDump;
@@ -323,6 +327,8 @@
     private final ActivityManagerService mService;
     private final Handler mBgHandler;
 
+    final CachedAppsWatermarkData mCachedAppsWatermarkData = new CachedAppsWatermarkData();
+
     /**
      * The lock to guard some of the profiling data here and {@link ProcessProfileRecord}.
      *
@@ -391,6 +397,193 @@
         }
     }
 
+    /**
+     * A simple data class holding the information about the cached apps high watermark.
+     *
+     * Keep it sync with the frameworks/proto_logging/stats/atoms.proto
+     */
+    class CachedAppsWatermarkData {
+        /** The high water mark of the number of cached apps. */
+        @GuardedBy("mProcLock")
+        int mCachedAppHighWatermark;
+
+        /**
+         * The uptime (in seconds) at the high watermark.
+         * Note this is going to be pull metrics, so we'll need the timestamp here.
+         */
+        @GuardedBy("mProcLock")
+        int mUptimeInSeconds;
+
+        /** The number of binder proxy at that high water mark. */
+        @GuardedBy("mProcLock")
+        int mBinderProxySnapshot;
+
+        /** Free physical memory (in kb) on device. */
+        @GuardedBy("mProcLock")
+        int mFreeInKb;
+
+        /** Cched physical memory (in kb) on device. */
+        @GuardedBy("mProcLock")
+        int mCachedInKb;
+
+        /** zram (in kb) on device. */
+        @GuardedBy("mProcLock")
+        int mZramInKb;
+
+        /** Kernel memory (in kb) on device. */
+        @GuardedBy("mProcLock")
+        int mKernelInKb;
+
+        /** The number of apps in frozen state. */
+        @GuardedBy("mProcLock")
+        int mNumOfFrozenApps;
+
+        /** The longest frozen time (now - last_frozen) in current frozen apps. */
+        @GuardedBy("mProcLock")
+        int mLongestFrozenTimeInSeconds;
+
+        /** The shortest frozen time (now - last_frozen) in current frozen apps. */
+        @GuardedBy("mProcLock")
+        int mShortestFrozenTimeInSeconds;
+
+        /** The mean frozen time (now - last_frozen) in current frozen apps. */
+        @GuardedBy("mProcLock")
+        int mMeanFrozenTimeInSeconds;
+
+        /** The average frozen time (now - last_frozen) in current frozen apps. */
+        @GuardedBy("mProcLock")
+        int mAverageFrozenTimeInSeconds;
+
+        /**
+         * This is an array holding the frozen app durations temporarily
+         * while updating the cached app high watermark.
+         */
+        @GuardedBy("mProcLock")
+        private long[] mCachedAppFrozenDurations;
+
+        /**
+         * The earliest frozen timestamp within the frozen apps.
+         */
+        @GuardedBy("mProcLock")
+        private long mEarliestFrozenTimestamp;
+
+        /**
+         * The most recent frozen timestamp within the frozen apps.
+         */
+        @GuardedBy("mProcLock")
+        private long mLatestFrozenTimestamp;
+
+        /**
+         * The sum of total frozen durations of all frozen apps.
+         */
+        @GuardedBy("mProcLock")
+        private long mTotalFrozenDurations;
+
+        @GuardedBy("mProcLock")
+        void updateCachedAppsHighWatermarkIfNecessaryLocked(int numOfCachedApps, long now) {
+            if (numOfCachedApps > mCachedAppHighWatermark) {
+                mCachedAppHighWatermark = numOfCachedApps;
+                mUptimeInSeconds = (int) (now / 1000);
+
+                // The rest of the updates are pretty costly, do it in a separated handler.
+                mService.mHandler.removeMessages(
+                        ActivityManagerService.UPDATE_CACHED_APP_HIGH_WATERMARK);
+                mService.mHandler.obtainMessage(
+                        ActivityManagerService.UPDATE_CACHED_APP_HIGH_WATERMARK, Long.valueOf(now))
+                        .sendToTarget();
+            }
+        }
+
+        void updateCachedAppsSnapshot(long now) {
+            synchronized (mProcLock) {
+                mEarliestFrozenTimestamp = now;
+                mLatestFrozenTimestamp = 0L;
+                mTotalFrozenDurations = 0L;
+                mNumOfFrozenApps = 0;
+                if (mCachedAppFrozenDurations == null
+                        || mCachedAppFrozenDurations.length < mCachedAppHighWatermark) {
+                    mCachedAppFrozenDurations = new long[Math.max(
+                            mCachedAppHighWatermark, mService.mConstants.CUR_MAX_CACHED_PROCESSES)];
+                }
+                mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
+                    if (app.mOptRecord.isFrozen()) {
+                        final long freezeTime = app.mOptRecord.getFreezeUnfreezeTime();
+                        if (freezeTime < mEarliestFrozenTimestamp) {
+                            mEarliestFrozenTimestamp = freezeTime;
+                        }
+                        if (freezeTime > mLatestFrozenTimestamp) {
+                            mLatestFrozenTimestamp = freezeTime;
+                        }
+                        final long duration = now - freezeTime;
+                        mTotalFrozenDurations += duration;
+                        mCachedAppFrozenDurations[mNumOfFrozenApps++] = duration;
+                    }
+                });
+                if (mNumOfFrozenApps > 0) {
+                    mLongestFrozenTimeInSeconds = (int) ((now - mEarliestFrozenTimestamp) / 1000);
+                    mShortestFrozenTimeInSeconds = (int) ((now - mLatestFrozenTimestamp) / 1000);
+                    mAverageFrozenTimeInSeconds =
+                            (int) ((mTotalFrozenDurations / mNumOfFrozenApps) / 1000);
+                    mMeanFrozenTimeInSeconds = (int) (QuickSelect.select(mCachedAppFrozenDurations,
+                            0, mNumOfFrozenApps, mNumOfFrozenApps / 2) / 1000);
+                }
+
+                mBinderProxySnapshot = 0;
+                final SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+                if (counts != null) {
+                    for (int i = 0, size = counts.size(); i < size; i++) {
+                        final int uid = counts.keyAt(i);
+                        final UidRecord uidRec = mService.mProcessList.getUidRecordLOSP(uid);
+                        if (uidRec != null) {
+                            mBinderProxySnapshot += counts.valueAt(i);
+                        }
+                    }
+                }
+
+                final MemInfoReader memInfo = new MemInfoReader();
+                memInfo.readMemInfo();
+                mFreeInKb = (int) memInfo.getFreeSizeKb();
+                mCachedInKb = (int) memInfo.getCachedSizeKb();
+                mZramInKb = (int) memInfo.getZramTotalSizeKb();
+                mKernelInKb = (int) memInfo.getKernelUsedSizeKb();
+            }
+        }
+
+        @NonNull
+        StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull) {
+            synchronized (mProcLock) {
+                final StatsEvent event = FrameworkStatsLog.buildStatsEvent(atomTag,
+                        mCachedAppHighWatermark,
+                        mUptimeInSeconds,
+                        mBinderProxySnapshot,
+                        mFreeInKb,
+                        mCachedInKb,
+                        mZramInKb,
+                        mKernelInKb,
+                        mNumOfFrozenApps,
+                        mLongestFrozenTimeInSeconds,
+                        mShortestFrozenTimeInSeconds,
+                        mMeanFrozenTimeInSeconds,
+                        mAverageFrozenTimeInSeconds);
+                if (resetAfterPull) {
+                    mCachedAppHighWatermark = 0;
+                    mUptimeInSeconds = 0;
+                    mBinderProxySnapshot = 0;
+                    mFreeInKb = 0;
+                    mCachedInKb = 0;
+                    mZramInKb = 0;
+                    mKernelInKb = 0;
+                    mNumOfFrozenApps = 0;
+                    mLongestFrozenTimeInSeconds = 0;
+                    mShortestFrozenTimeInSeconds = 0;
+                    mMeanFrozenTimeInSeconds = 0;
+                    mAverageFrozenTimeInSeconds = 0;
+                }
+                return event;
+            }
+        }
+    }
+
     private class BgHandler extends Handler {
         static final int COLLECT_PSS_BG_MSG = 1;
         static final int DEFER_PSS_MSG = 2;
@@ -954,7 +1147,7 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    boolean updateLowMemStateLSP(int numCached, int numEmpty, int numTrimming) {
+    boolean updateLowMemStateLSP(int numCached, int numEmpty, int numTrimming, long now) {
         int memFactor;
         if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
             memFactor = mLowMemDetector.getMemFactor();
@@ -1040,11 +1233,10 @@
         mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
         boolean allChanged;
         int trackerMemFactor;
-        final long now;
         synchronized (mService.mProcessStats.mLock) {
-            now = SystemClock.uptimeMillis();
             allChanged = mService.mProcessStats.setMemFactorLocked(memFactor,
-                    mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
+                    mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(),
+                    SystemClock.uptimeMillis() /* re-acquire the time within the lock */);
             trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
         }
         if (memFactor != ADJ_MEM_FACTOR_NORMAL) {
@@ -1124,6 +1316,8 @@
                 profile.setTrimMemoryLevel(0);
             });
         }
+        mCachedAppsWatermarkData.updateCachedAppsHighWatermarkIfNecessaryLocked(
+                numCached + numEmpty, now);
         return allChanged;
     }
 
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index 993595b..8f84b08 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -190,6 +190,10 @@
         // and also clean up the start calls stack by UID
         final ArrayList<Integer> apiTypes = convertFgsTypeToApiTypes(record.foregroundServiceType);
         final UidState uidState = mUids.get(uid);
+        if (uidState == null) {
+            Log.e(TAG, "FGS stop call being logged with no start call for UID " + uid);
+            return;
+        }
         final ArrayList<Integer> apisFound = new ArrayList<>();
         final ArrayList<Long> timestampsFound = new ArrayList<>();
         for (int i = 0, size = apiTypes.size(); i < size; i++) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 365dcd9..1e5f187 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1401,7 +1401,7 @@
 
         mLastFreeSwapPercent = freeSwapPercent;
 
-        return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming);
+        return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming, now);
     }
 
     @GuardedBy({"mService", "mProcLock"})
@@ -1482,7 +1482,10 @@
                         }
                         if (uidRec.isIdle() && !uidRec.isSetIdle()) {
                             uidChange |= UidRecord.CHANGE_IDLE;
-                            becameIdle.add(uidRec);
+                            if (uidRec.getSetProcState() != PROCESS_STATE_NONEXISTENT) {
+                                // don't stop the bg services if it's just started.
+                                becameIdle.add(uidRec);
+                            }
                         }
                     } else {
                         if (uidRec.isIdle()) {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index ca41f42..e498384 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -290,7 +290,7 @@
             String parentShortComponentName, WindowProcessController parentProcess,
             boolean aboveSystem, TimeoutRecord timeoutRecord,
             ExecutorService auxiliaryTaskExecutor, boolean onlyDumpSelf,
-            boolean isContinuousAnr) {
+            boolean isContinuousAnr, Future<File> firstPidFilePromise) {
         String annotation = timeoutRecord.mReason;
         AnrLatencyTracker latencyTracker = timeoutRecord.mLatencyTracker;
         Future<?> updateCpuStatsNowFirstCall = null;
@@ -335,7 +335,6 @@
                 Counter.logIncrement("stability_anr.value_skipped_anrs");
                 return;
             }
-
             // In case we come through here for the same app before completing
             // this one, mark as anring now so we will bail out.
             latencyTracker.waitingOnProcLockStarted();
@@ -369,6 +368,9 @@
             firstPids.add(pid);
 
             // Don't dump other PIDs if it's a background ANR or is requested to only dump self.
+            // Note that the primary pid is added here just in case, as it should normally be
+            // dumped on the early dump thread, and would only be dumped on the Anr consumer thread
+            // as a fallback.
             isSilentAnr = isSilentAnr();
             if (!isSilentAnr && !onlyDumpSelf) {
                 int parentPid = pid;
@@ -501,7 +503,8 @@
         File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
                 isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
                 nativePidsFuture, tracesFileException, firstPidEndOffset, annotation,
-                criticalEventLog, memoryHeaders, auxiliaryTaskExecutor, latencyTracker);
+                criticalEventLog, memoryHeaders, auxiliaryTaskExecutor, firstPidFilePromise,
+                latencyTracker);
 
         if (isMonitorCpuUsage()) {
             // Wait for the first call to finish
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 312f98a..d7b22a8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -108,6 +108,7 @@
 import android.os.UserHandle;
 import android.os.storage.StorageManagerInternal;
 import android.system.Os;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -2331,9 +2332,15 @@
 
             if (!regularZygote) {
                 // webview and app zygote don't have the permission to create the nodes
-                if (Process.createProcessGroup(uid, startResult.pid) < 0) {
-                    throw new AssertionError("Unable to create process group for " + app.processName
-                            + " (" + startResult.pid + ")");
+                final int res = Process.createProcessGroup(uid, startResult.pid);
+                if (res < 0) {
+                    if (res == -OsConstants.ESRCH) {
+                        Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
+                            + app.processName + " (" + startResult.pid + ")");
+                    } else {
+                        throw new AssertionError("Unable to create process group for "
+                            + app.processName + " (" + startResult.pid + ")");
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index a875860..78aafeb 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -176,6 +176,13 @@
     boolean mAllowWhileInUsePermissionInFgs;
     @PowerExemptionManager.ReasonCode
     int mAllowWhileInUsePermissionInFgsReason;
+
+    // Integer version of mAllowWhileInUsePermissionInFgs that we keep track to compare
+    // the old and new logics.
+    // TODO: Remove them once we're confident in the new logic.
+    int mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
+    int mDebugWhileInUseReasonInBindService = REASON_DENIED;
+
     // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
     boolean mAllowWhileInUsePermissionInFgsAtEntering;
     /** Allow scheduling user-initiated jobs from the background. */
@@ -216,8 +223,13 @@
     // created. (i.e. due to "bound" or "start".) It never decreases, even when stopForeground()
     // is called.
     int mStartForegroundCount;
-    // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground is set.
-    long mLastSetFgsRestrictionTime;
+
+    // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground was set to "allowed"
+    // from "disallowed" when the service was _not_ already a foreground service.
+    // this means they're set in startService(). (not startForegroundService)
+    // In startForeground(), if this timestamp is too old, we can't trust these flags, so
+    // we need to reset them.
+    long mLastUntrustedSetFgsRestrictionAllowedTime;
 
     // This is a service record of a FGS delegate (not a service record of a real service)
     boolean mIsFgsDelegate;
@@ -609,10 +621,14 @@
             pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
             pw.println(mBackgroundStartPrivilegesByStartMerged);
         }
-        pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
-                pw.println(mAllowWhileInUsePermissionInFgs);
         pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
         pw.println(mAllowWhileInUsePermissionInFgsReason);
+
+        pw.print(prefix); pw.print("debugWhileInUseReasonInStartForeground=");
+        pw.println(mDebugWhileInUseReasonInStartForeground);
+        pw.print(prefix); pw.print("debugWhileInUseReasonInBindService=");
+        pw.println(mDebugWhileInUseReasonInBindService);
+
         pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
         pw.print(prefix); pw.print("recentCallingPackage=");
                 pw.println(mRecentCallingPackage);
@@ -624,6 +640,10 @@
         pw.println(mStartForegroundCount);
         pw.print(prefix); pw.print("infoAllowStartForeground=");
         pw.println(mInfoAllowStartForeground);
+
+        pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime=");
+        TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw);
+
         if (delayed) {
             pw.print(prefix); pw.print("delayed="); pw.println(delayed);
         }
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index 10ddc2f..01fb0d1 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -41,6 +41,7 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -50,6 +51,8 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Supplier;
 
@@ -65,11 +68,16 @@
             new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
 
     static final String ANR_FILE_PREFIX = "anr_";
-    public static final String ANR_TRACE_DIR = "/data/anr";
+    static final String ANR_TEMP_FILE_PREFIX = "temp_anr_";
 
+    public static final String ANR_TRACE_DIR = "/data/anr";
     private static final int NATIVE_DUMP_TIMEOUT_MS =
             2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
     private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
+    // The time limit for a single process's dump
+    private static final int TEMP_DUMP_TIME_LIMIT =
+            10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 10 seconds
+
 
     /**
      * If a stack trace dump file is configured, dump process stack traces.
@@ -85,7 +93,7 @@
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
             @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
         return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
-                logExceptionCreatingFile, null, null, null, null, auxiliaryTaskExecutor,
+                logExceptionCreatingFile, null, null, null, null, auxiliaryTaskExecutor, null,
                 latencyTracker);
     }
 
@@ -96,11 +104,11 @@
     public static File dumpStackTraces(ArrayList<Integer> firstPids,
             ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
-            String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor,
-            AnrLatencyTracker latencyTracker) {
+            String subject, String criticalEventSection,
+            @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
         return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
                 logExceptionCreatingFile, null, subject, criticalEventSection,
-                /* memoryHeaders= */ null, auxiliaryTaskExecutor, latencyTracker);
+                /* memoryHeaders= */ null, auxiliaryTaskExecutor, null, latencyTracker);
     }
 
     /**
@@ -112,7 +120,7 @@
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
             AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
             String memoryHeaders, @NonNull Executor auxiliaryTaskExecutor,
-            AnrLatencyTracker latencyTracker) {
+            Future<File> firstPidFilePromise, AnrLatencyTracker latencyTracker) {
         try {
 
             if (latencyTracker != null) {
@@ -161,7 +169,7 @@
 
             long firstPidEndPos = dumpStackTraces(
                     tracesFile.getAbsolutePath(), firstPids, nativePidsFuture,
-                    extraPidsFuture, latencyTracker);
+                    extraPidsFuture, firstPidFilePromise, latencyTracker);
             if (firstPidEndOffset != null) {
                 firstPidEndOffset.set(firstPidEndPos);
             }
@@ -175,7 +183,6 @@
                 latencyTracker.dumpStackTracesEnded();
             }
         }
-
     }
 
     /**
@@ -183,7 +190,8 @@
      */
     public static long dumpStackTraces(String tracesFile,
             ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture,
-            Future<ArrayList<Integer>> extraPidsFuture, AnrLatencyTracker latencyTracker) {
+            Future<ArrayList<Integer>> extraPidsFuture, Future<File> firstPidFilePromise,
+            AnrLatencyTracker latencyTracker) {
 
         Slog.i(TAG, "Dumping to " + tracesFile);
 
@@ -194,33 +202,52 @@
         // We must complete all stack dumps within 20 seconds.
         long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
-        // As applications are usually interested with the ANR stack traces, but we can't share with
-        // them the stack traces other than their own stacks. So after the very first PID is
+        // As applications are usually interested with the ANR stack traces, but we can't share
+        // with them the stack traces other than their own stacks. So after the very first PID is
         // dumped, remember the current file size.
         long firstPidEnd = -1;
 
-        // First collect all of the stacks of the most important pids.
-        if (firstPids != null) {
+        // Was the first pid copied from the temporary file that was created in the predump phase?
+        boolean firstPidTempDumpCopied = false;
+
+        // First copy the first pid's dump from the temporary file it was dumped into earlier,
+        // The first pid should always exist in firstPids but we check the size just in case.
+        if (firstPidFilePromise != null && firstPids != null && firstPids.size() > 0) {
+            final int primaryPid = firstPids.get(0);
+            final long start = SystemClock.elapsedRealtime();
+            firstPidTempDumpCopied = copyFirstPidTempDump(tracesFile, firstPidFilePromise,
+                    remainingTime, latencyTracker);
+            final long timeTaken = SystemClock.elapsedRealtime() - start;
+            remainingTime -= timeTaken;
+            if (remainingTime <= 0) {
+                Slog.e(TAG, "Aborting stack trace dump (currently copying primary pid" + primaryPid
+                        + "); deadline exceeded.");
+                return firstPidEnd;
+            }
+             // We don't copy ANR traces from the system_server intentionally.
+            if (firstPidTempDumpCopied && primaryPid != ActivityManagerService.MY_PID) {
+                firstPidEnd = new File(tracesFile).length();
+            }
+            // Append the Durations/latency comma separated array after the first PID.
+            if (latencyTracker != null) {
+                appendtoANRFile(tracesFile,
+                        latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
+            }
+        }
+        // Next collect all of the stacks of the most important pids.
+        if (firstPids != null)  {
             if (latencyTracker != null) {
                 latencyTracker.dumpingFirstPidsStarted();
             }
 
             int num = firstPids.size();
-            for (int i = 0; i < num; i++) {
+            for (int i = firstPidTempDumpCopied ? 1 : 0; i < num; i++) {
                 final int pid = firstPids.get(i);
                 // We don't copy ANR traces from the system_server intentionally.
                 final boolean firstPid = i == 0 && ActivityManagerService.MY_PID != pid;
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidStarted(pid);
-                }
-
                 Slog.i(TAG, "Collecting stacks for pid " + pid);
-                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
-                                                                remainingTime);
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidEnded();
-                }
-
+                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime,
+                        latencyTracker);
                 remainingTime -= timeTaken;
                 if (remainingTime <= 0) {
                     Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
@@ -304,13 +331,8 @@
             }
             for (int pid : extraPids) {
                 Slog.i(TAG, "Collecting stacks for extra pid " + pid);
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidStarted(pid);
-                }
-                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidEnded();
-                }
+                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime,
+                        latencyTracker);
                 remainingTime -= timeTaken;
                 if (remainingTime <= 0) {
                     Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid
@@ -333,6 +355,99 @@
         return firstPidEnd;
     }
 
+    /**
+     * Dumps the supplied pid to a temporary file.
+     * @param pid the PID to be dumped
+     * @param latencyTracker the latency tracker instance of the current ANR.
+     */
+    public static File dumpStackTracesTempFile(int pid, AnrLatencyTracker latencyTracker) {
+        try {
+            if (latencyTracker != null) {
+                latencyTracker.dumpStackTracesTempFileStarted();
+            }
+
+            File tmpTracesFile;
+            try {
+                tmpTracesFile = File.createTempFile(ANR_TEMP_FILE_PREFIX, ".txt",
+                        new File(ANR_TRACE_DIR));
+                Slog.d(TAG, "created ANR temporary file:" + tmpTracesFile.getAbsolutePath());
+            } catch (IOException e) {
+                Slog.w(TAG, "Exception creating temporary ANR dump file:", e);
+                if (latencyTracker != null) {
+                    latencyTracker.dumpStackTracesTempFileCreationFailed();
+                }
+                return null;
+            }
+
+            Slog.i(TAG, "Collecting stacks for pid " + pid + " into temporary file "
+                    + tmpTracesFile.getName());
+            if (latencyTracker != null) {
+                latencyTracker.dumpingPidStarted(pid);
+            }
+            final long timeTaken = dumpJavaTracesTombstoned(pid, tmpTracesFile.getAbsolutePath(),
+                    TEMP_DUMP_TIME_LIMIT);
+            if (latencyTracker != null) {
+                latencyTracker.dumpingPidEnded();
+            }
+            if (TEMP_DUMP_TIME_LIMIT <= timeTaken) {
+                Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid
+                        + "); deadline exceeded.");
+                tmpTracesFile.delete();
+                if (latencyTracker != null) {
+                    latencyTracker.dumpStackTracesTempFileTimedOut();
+                }
+                return null;
+            }
+            if (DEBUG_ANR) {
+                Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms"
+                        + " dumped into temporary file " + tmpTracesFile.getName());
+            }
+            return tmpTracesFile;
+        } finally {
+            if (latencyTracker != null) {
+                latencyTracker.dumpStackTracesTempFileEnded();
+            }
+        }
+    }
+
+    private static boolean copyFirstPidTempDump(String tracesFile, Future<File> firstPidFilePromise,
+            long timeLimitMs, AnrLatencyTracker latencyTracker) {
+
+        boolean copySucceeded = false;
+        try (FileOutputStream fos = new FileOutputStream(tracesFile, true))  {
+            if (latencyTracker != null) {
+                latencyTracker.copyingFirstPidStarted();
+            }
+            final File tempfile = firstPidFilePromise.get(timeLimitMs, TimeUnit.MILLISECONDS);
+            if (tempfile != null) {
+                Files.copy(tempfile.toPath(), fos);
+                // Delete the temporary first pid dump file
+                tempfile.delete();
+                copySucceeded = true;
+                return copySucceeded;
+            }
+            return false;
+        } catch (ExecutionException e) {
+            Slog.w(TAG, "Failed to collect the first pid's predump to the main ANR file",
+                    e.getCause());
+            return false;
+        } catch (InterruptedException e) {
+            Slog.w(TAG, "Interrupted while collecting the first pid's predump"
+                    + " to the main ANR file", e);
+            return false;
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to read the first pid's predump file", e);
+            return false;
+        } catch (TimeoutException e) {
+            Slog.w(TAG, "Copying the first pid timed out", e);
+            return false;
+        } finally {
+            if (latencyTracker != null) {
+                latencyTracker.copyingFirstPidEnded(copySucceeded);
+            }
+        }
+    }
+
     private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
         final String formattedDate = ANR_FILE_DATE_FORMAT.format(new Date());
         final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
@@ -409,6 +524,21 @@
             Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
         }
     }
+
+    private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs,
+            AnrLatencyTracker latencyTracker) {
+        try {
+            if (latencyTracker != null) {
+                latencyTracker.dumpingPidStarted(pid);
+            }
+            return dumpJavaTracesTombstoned(pid, fileName, timeoutMs);
+        } finally {
+            if (latencyTracker != null) {
+                latencyTracker.dumpingPidEnded();
+            }
+        }
+    }
+
     /**
      * Dump java traces for process {@code pid} to the specified file. If java trace dumping
      * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 462942e..2da9fa6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -87,14 +87,14 @@
     private final @NonNull Context mContext;
 
     /** ID for Communication strategy retrieved form audio policy manager */
-    /*package*/  int mCommunicationStrategyId = -1;
+    private int mCommunicationStrategyId = -1;
 
     /** ID for Accessibility strategy retrieved form audio policy manager */
     private int mAccessibilityStrategyId = -1;
 
 
     /** Active communication device reported by audio policy manager */
-    /*package*/ AudioDeviceInfo mActiveCommunicationDevice;
+    private AudioDeviceInfo mActiveCommunicationDevice;
     /** Last preferred device set for communication strategy */
     private AudioDeviceAttributes mPreferredCommunicationDevice;
 
@@ -750,19 +750,6 @@
             mIsLeOutput = false;
         }
 
-        BtDeviceInfo(@NonNull BtDeviceInfo src, int state) {
-            mDevice = src.mDevice;
-            mState = state;
-            mProfile = src.mProfile;
-            mSupprNoisy = src.mSupprNoisy;
-            mVolume = src.mVolume;
-            mIsLeOutput = src.mIsLeOutput;
-            mEventSource = src.mEventSource;
-            mAudioSystemDevice = src.mAudioSystemDevice;
-            mMusicDevice = src.mMusicDevice;
-            mCodec = src.mCodec;
-        }
-
         // redefine equality op so we can match messages intended for this device
         @Override
         public boolean equals(Object o) {
@@ -829,7 +816,7 @@
      * @param info struct with the (dis)connection information
      */
     /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
-        if (data.mPreviousDevice != null
+        if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
                 && data.mPreviousDevice.equals(data.mNewDevice)) {
             final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
             new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
@@ -838,8 +825,7 @@
                     .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
                     .record();
             synchronized (mDeviceStateLock) {
-                postBluetoothDeviceConfigChange(createBtDeviceInfo(data, data.mNewDevice,
-                        BluetoothProfile.STATE_CONNECTED));
+                postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
             }
         } else {
             synchronized (mDeviceStateLock) {
@@ -1064,8 +1050,8 @@
                 new AudioModeInfo(mode, pid, uid));
     }
 
-    /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
-        sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
+    /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
+        sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
     }
 
     /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
@@ -1322,10 +1308,6 @@
         sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state);
     }
 
-    /*package*/ void postNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) {
-        sendLMsgNoDelay(MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED, SENDMSG_QUEUE, btDevice);
-    }
-
     /*package*/ static final class CommunicationDeviceInfo {
         final @NonNull IBinder mCb; // Identifies the requesting client for death handler
         final int mPid; // Requester process ID
@@ -1401,11 +1383,9 @@
         }
     }
 
-    /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes,
-                                boolean connect, @Nullable BluetoothDevice btDevice) {
+    /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) {
         synchronized (mDeviceStateLock) {
-            return mDeviceInventory.handleDeviceConnection(
-                    attributes, connect, false /*for test*/, btDevice);
+            return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/);
         }
     }
 
@@ -1646,10 +1626,13 @@
                                 (String) msg.obj, msg.arg1);
                     }
                     break;
-                case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
+                case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+                    final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
                     synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onBluetoothDeviceConfigChange(
-                                (BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+                        final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
+                        mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
+                                new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
+                                        BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
                     }
                     break;
                 case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
@@ -1807,10 +1790,6 @@
                     final int capturePreset = msg.arg1;
                     mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
                 } break;
-                case MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED: {
-                    final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
-                    BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
-                } break;
                 default:
                     Log.wtf(TAG, "Invalid message " + msg.what);
             }
@@ -1846,7 +1825,7 @@
     private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
 
     // process change of A2DP device configuration, obj is BluetoothDevice
-    private static final int MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE = 11;
+    private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11;
 
     private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12;
     private static final int MSG_REPORT_NEW_ROUTES = 13;
@@ -1886,15 +1865,13 @@
     private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
     private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
 
-    private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 50;
-
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
             case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
             case MSG_L_SET_BT_ACTIVE_DEVICE:
             case MSG_IL_BTA2DP_TIMEOUT:
             case MSG_IL_BTLEAUDIO_TIMEOUT:
-            case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
+            case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
             case MSG_TOGGLE_HDMI:
             case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
             case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1986,7 +1963,7 @@
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                 case MSG_IL_BTA2DP_TIMEOUT:
                 case MSG_IL_BTLEAUDIO_TIMEOUT:
-                case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
+                case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                     if (sLastDeviceConnectMsgTime >= time) {
                         // add a little delay to make sure messages are ordered as expected
                         time = sLastDeviceConnectMsgTime + 30;
@@ -2006,7 +1983,7 @@
     static {
         MESSAGES_MUTE_MUSIC = new HashSet<>();
         MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE);
-        MESSAGES_MUTE_MUSIC.add(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE);
+        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE);
         MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT);
         MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE);
     }
@@ -2027,7 +2004,7 @@
         // Do not mute on bluetooth event if music is playing on a wired headset.
         if ((message == MSG_L_SET_BT_ACTIVE_DEVICE
                 || message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT
-                || message == MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE)
+                || message == MSG_L_A2DP_DEVICE_CONFIG_CHANGE)
                 && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
                 && hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
                         mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
@@ -2166,19 +2143,18 @@
         if (preferredCommunicationDevice == null) {
             AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice();
             if (defaultDevice != null) {
-                mDeviceInventory.setPreferredDevicesForStrategy(
+                setPreferredDevicesForStrategySync(
                         mCommunicationStrategyId, Arrays.asList(defaultDevice));
-                mDeviceInventory.setPreferredDevicesForStrategy(
+                setPreferredDevicesForStrategySync(
                         mAccessibilityStrategyId, Arrays.asList(defaultDevice));
             } else {
-                mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId);
-                mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId);
+                removePreferredDevicesForStrategySync(mCommunicationStrategyId);
+                removePreferredDevicesForStrategySync(mAccessibilityStrategyId);
             }
-            mDeviceInventory.applyConnectedDevicesRoles();
         } else {
-            mDeviceInventory.setPreferredDevicesForStrategy(
+            setPreferredDevicesForStrategySync(
                     mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
-            mDeviceInventory.setPreferredDevicesForStrategy(
+            setPreferredDevicesForStrategySync(
                     mAccessibilityStrategyId, Arrays.asList(preferredCommunicationDevice));
         }
         onUpdatePhoneStrategyDevice(preferredCommunicationDevice);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 1eb39f7..228bc87 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -34,36 +34,26 @@
 import android.media.IStrategyNonDefaultDevicesDispatcher;
 import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
-import android.media.MediaRecorder.AudioSource;
-import android.media.audiopolicy.AudioProductStrategy;
 import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.SafeCloseable;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.utils.EventLogger;
 
-import com.google.android.collect.Sets;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
@@ -185,26 +175,18 @@
     final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
             new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
 
-    final List<AudioProductStrategy> mStrategies;
-
     /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
-        this(broker, AudioSystemAdapter.getDefaultAdapter());
+        mDeviceBroker = broker;
+        mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
     }
 
     //-----------------------------------------------------------
     /** for mocking only, allows to inject AudioSystem adapter */
     /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) {
-        this(null, audioSystem);
+        mDeviceBroker = null;
+        mAudioSystem = audioSystem;
     }
 
-    private AudioDeviceInventory(@Nullable AudioDeviceBroker broker,
-                       @Nullable AudioSystemAdapter audioSystem) {
-        mDeviceBroker = broker;
-        mAudioSystem = audioSystem;
-        mStrategies = AudioProductStrategy.getAudioProductStrategies();
-        mBluetoothDualModeEnabled = SystemProperties.getBoolean(
-                "persist.bluetooth.enable_dual_mode_audio", false);
-    }
     /*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) {
         mDeviceBroker = broker;
     }
@@ -221,13 +203,8 @@
         int mDeviceCodecFormat;
         final UUID mSensorUuid;
 
-        /** Disabled operating modes for this device. Use a negative logic so that by default
-         * an empty list means all modes are allowed.
-         * See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */
-        @NonNull ArraySet<String> mDisabledModes = new ArraySet(0);
-
         DeviceInfo(int deviceType, String deviceName, String deviceAddress,
-                   int deviceCodecFormat, @Nullable UUID sensorUuid) {
+                   int deviceCodecFormat, UUID sensorUuid) {
             mDeviceType = deviceType;
             mDeviceName = deviceName == null ? "" : deviceName;
             mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
@@ -235,31 +212,11 @@
             mSensorUuid = sensorUuid;
         }
 
-        void setModeDisabled(String mode) {
-            mDisabledModes.add(mode);
-        }
-        void setModeEnabled(String mode) {
-            mDisabledModes.remove(mode);
-        }
-        boolean isModeEnabled(String mode) {
-            return !mDisabledModes.contains(mode);
-        }
-        boolean isOutputOnlyModeEnabled() {
-            return isModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY);
-        }
-        boolean isDuplexModeEnabled() {
-            return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
-        }
-
         DeviceInfo(int deviceType, String deviceName, String deviceAddress,
                    int deviceCodecFormat) {
             this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
         }
 
-        DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
-            this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT);
-        }
-
         @Override
         public String toString() {
             return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
@@ -267,8 +224,7 @@
                     + ") name:" + mDeviceName
                     + " addr:" + mDeviceAddress
                     + " codec: " + Integer.toHexString(mDeviceCodecFormat)
-                    + " sensorUuid: " + Objects.toString(mSensorUuid)
-                    + " disabled modes: " + mDisabledModes + "]";
+                    + " sensorUuid: " + Objects.toString(mSensorUuid) + "]";
         }
 
         @NonNull String getKey() {
@@ -320,18 +276,9 @@
             pw.println("  " + prefix + " type:0x" + Integer.toHexString(keyType)
                     + " (" + AudioSystem.getDeviceName(keyType)
                     + ") addr:" + valueAddress); });
-        pw.println("\n" + prefix + "Preferred devices for capture preset:");
         mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
             pw.println("  " + prefix + "capturePreset:" + capturePreset
                     + " devices:" + devices); });
-        pw.println("\n" + prefix + "Applied devices roles for strategies:");
-        mAppliedStrategyRoles.forEach((key, devices) -> {
-            pw.println("  " + prefix + "strategy: " + key.first
-                    +  " role:" + key.second + " devices:" + devices); });
-        pw.println("\n" + prefix + "Applied devices roles for presets:");
-        mAppliedPresetRoles.forEach((key, devices) -> {
-            pw.println("  " + prefix + "preset: " + key.first
-                    +  " role:" + key.second + " devices:" + devices); });
     }
 
     //------------------------------------------------------------
@@ -352,16 +299,15 @@
                         AudioSystem.DEVICE_STATE_AVAILABLE,
                         di.mDeviceCodecFormat);
             }
-            mAppliedStrategyRoles.clear();
-            applyConnectedDevicesRoles_l();
         }
         synchronized (mPreferredDevices) {
             mPreferredDevices.forEach((strategy, devices) -> {
-                setPreferredDevicesForStrategy(strategy, devices); });
+                mAudioSystem.setDevicesRoleForStrategy(
+                        strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
         }
         synchronized (mNonDefaultDevices) {
             mNonDefaultDevices.forEach((strategy, devices) -> {
-                addDevicesRoleForStrategy(
+                mAudioSystem.setDevicesRoleForStrategy(
                         strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); });
         }
         synchronized (mPreferredDevicesForCapturePreset) {
@@ -434,7 +380,8 @@
                                     btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
                                     "onSetBtActiveDevice");
                         }
-                        makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice");
+                        makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+                                "onSetBtActiveDevice", btInfo.mCodec);
                     }
                     break;
                 case BluetoothProfile.HEARING_AID:
@@ -450,7 +397,10 @@
                     if (switchToUnavailable) {
                         makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
                     } else if (switchToAvailable) {
-                        makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice");
+                        makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+                                streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
+                                btInfo.mAudioSystemDevice,
+                                "onSetBtActiveDevice");
                     }
                     break;
                 default: throw new IllegalArgumentException("Invalid profile "
@@ -461,30 +411,30 @@
 
 
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ void onBluetoothDeviceConfigChange(
-            @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) {
+        /*package*/ void onBluetoothA2dpDeviceConfigChange(
+            @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
-                + "onBluetoothDeviceConfigChange")
-                .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
+                + "onBluetoothA2dpDeviceConfigChange")
+                .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event));
 
-        final BluetoothDevice btDevice = btInfo.mDevice;
+        final BluetoothDevice btDevice = btInfo.getBtDevice();
         if (btDevice == null) {
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record();
             return;
         }
         if (AudioService.DEBUG_DEVICES) {
-            Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
+            Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
         }
-        int volume = btInfo.mVolume;
-        @AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec;
+        int a2dpVolume = btInfo.getVolume();
+        @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec();
 
         String address = btDevice.getAddress();
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
         AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
-                "onBluetoothDeviceConfigChange addr=" + address
-                    + " event=" + BtHelper.deviceEventToString(event)));
+                "onBluetoothA2dpDeviceConfigChange addr=" + address
+                    + " event=" + BtHelper.a2dpDeviceEventToString(event)));
 
         synchronized (mDevicesLock) {
             if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
@@ -499,55 +449,54 @@
                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
             final DeviceInfo di = mConnectedDevices.get(key);
             if (di == null) {
-                Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange");
+                Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange");
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
                 return;
             }
 
             mmi.set(MediaMetrics.Property.ADDRESS, address)
                     .set(MediaMetrics.Property.ENCODING,
-                            AudioSystem.audioFormatToString(audioCodec))
-                    .set(MediaMetrics.Property.INDEX, volume)
+                            AudioSystem.audioFormatToString(a2dpCodec))
+                    .set(MediaMetrics.Property.INDEX, a2dpVolume)
                     .set(MediaMetrics.Property.NAME, di.mDeviceName);
 
-
-            if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
-                boolean a2dpCodecChange = false;
-                if (btInfo.mProfile == BluetoothProfile.A2DP) {
-                    if (di.mDeviceCodecFormat != audioCodec) {
-                        di.mDeviceCodecFormat = audioCodec;
-                        mConnectedDevices.replace(key, di);
-                        a2dpCodecChange = true;
-                    }
-                    final int res = mAudioSystem.handleDeviceConfigChange(
-                            btInfo.mAudioSystemDevice, address,
-                            BtHelper.getName(btDevice), audioCodec);
-
-                    if (res != AudioSystem.AUDIO_STATUS_OK) {
-                        AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
-                                "APM handleDeviceConfigChange failed for A2DP device addr="
-                                        + address + " codec="
-                                        + AudioSystem.audioFormatToString(audioCodec))
-                                .printLog(TAG));
-
-                        // force A2DP device disconnection in case of error so that AudioService
-                        // state is consistent with audio policy manager state
-                        setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo,
-                                BluetoothProfile.STATE_DISCONNECTED));
-                    } else {
-                        AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
-                                "APM handleDeviceConfigChange success for A2DP device addr="
-                                        + address
-                                        + " codec=" + AudioSystem.audioFormatToString(audioCodec))
-                                .printLog(TAG));
-
-                    }
+            if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) {
+                // Device is connected
+                if (a2dpVolume != -1) {
+                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                            // convert index to internal representation in VolumeStreamState
+                            a2dpVolume * 10,
+                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                            "onBluetoothA2dpDeviceConfigChange");
                 }
-                if (!a2dpCodecChange) {
-                    updateBluetoothPreferredModes_l();
-                    mDeviceBroker.postNotifyPreferredAudioProfileApplied(btDevice);
+            } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
+                if (di.mDeviceCodecFormat != a2dpCodec) {
+                    di.mDeviceCodecFormat = a2dpCodec;
+                    mConnectedDevices.replace(key, di);
                 }
             }
+            final int res = mAudioSystem.handleDeviceConfigChange(
+                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address,
+                    BtHelper.getName(btDevice), a2dpCodec);
+
+            if (res != AudioSystem.AUDIO_STATUS_OK) {
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                        "APM handleDeviceConfigChange failed for A2DP device addr=" + address
+                                + " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
+                        .printLog(TAG));
+
+                int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
+                // force A2DP device disconnection in case of error so that AudioService state is
+                // consistent with audio policy manager state
+                setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice,
+                                BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
+                                musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+            } else {
+                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                        "APM handleDeviceConfigChange success for A2DP device addr=" + address
+                                + " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
+                        .printLog(TAG));
+            }
         }
         mmi.record();
     }
@@ -629,7 +578,7 @@
             }
 
             if (!handleDeviceConnection(wdcs.mAttributes,
-                    wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) {
+                    wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) {
                 // change of connection state failed, bailout
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
                         .record();
@@ -763,35 +712,23 @@
 
     /*package*/ int setPreferredDevicesForStrategySync(int strategy,
             @NonNull List<AudioDeviceAttributes> devices) {
-        final int status = setPreferredDevicesForStrategy(strategy, devices);
+        int status = AudioSystem.ERROR;
+
+        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
+                            "setPreferredDevicesForStrategySync, strategy: " + strategy
+                            + " devices: " + devices)).printLog(TAG));
+            status = mAudioSystem.setDevicesRoleForStrategy(
+                    strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        }
+
         if (status == AudioSystem.SUCCESS) {
             mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
         }
         return status;
     }
 
-    /*package*/ int setPreferredDevicesForStrategy(int strategy,
-            @NonNull List<AudioDeviceAttributes> devices) {
-        int status = AudioSystem.ERROR;
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
-                    "setPreferredDevicesForStrategy, strategy: " + strategy
-                            + " devices: " + devices)).printLog(TAG));
-            status = setDevicesRoleForStrategy(
-                    strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
-        }
-        return status;
-    }
-
     /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
-        final int status = removePreferredDevicesForStrategy(strategy);
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
-        }
-        return status;
-    }
-
-    /*package*/ int removePreferredDevicesForStrategy(int strategy) {
         int status = AudioSystem.ERROR;
 
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -799,9 +736,13 @@
                             "removePreferredDevicesForStrategySync, strategy: "
                             + strategy)).printLog(TAG));
 
-            status = clearDevicesRoleForStrategy(
+            status = mAudioSystem.clearDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_PREFERRED);
         }
+
+        if (status == AudioSystem.SUCCESS) {
+            mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
+        }
         return status;
     }
 
@@ -816,7 +757,7 @@
             AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                             "setDeviceAsNonDefaultForStrategySync, strategy: " + strategy
                             + " device: " + device)).printLog(TAG));
-            status = addDevicesRoleForStrategy(
+            status = mAudioSystem.setDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices);
         }
 
@@ -838,7 +779,7 @@
                             "removeDeviceAsNonDefaultForStrategySync, strategy: "
                             + strategy + " devices: " + device)).printLog(TAG));
 
-            status = removeDevicesRoleForStrategy(
+            status = mAudioSystem.removeDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices);
         }
 
@@ -871,70 +812,33 @@
 
     /*package*/ int setPreferredDevicesForCapturePresetSync(
             int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
-        final int status = setPreferredDevicesForCapturePreset(capturePreset, devices);
+        int status = AudioSystem.ERROR;
+
+        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+            status = mAudioSystem.setDevicesRoleForCapturePreset(
+                    capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        }
+
         if (status == AudioSystem.SUCCESS) {
             mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
         }
         return status;
     }
 
-    private int setPreferredDevicesForCapturePreset(
-            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
-        int status = AudioSystem.ERROR;
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            status = setDevicesRoleForCapturePreset(
-                    capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
-        }
-        return status;
-    }
-
     /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
-        final int status  = clearPreferredDevicesForCapturePreset(capturePreset);
+        int status  = AudioSystem.ERROR;
+
+        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+            status = mAudioSystem.clearDevicesRoleForCapturePreset(
+                    capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+        }
+
         if (status == AudioSystem.SUCCESS) {
             mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
         }
         return status;
     }
 
-    private int clearPreferredDevicesForCapturePreset(int capturePreset) {
-        int status  = AudioSystem.ERROR;
-
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            status = clearDevicesRoleForCapturePreset(
-                    capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
-        }
-        return status;
-    }
-
-    private int addDevicesRoleForCapturePreset(int capturePreset, int role,
-                                               @NonNull List<AudioDeviceAttributes> devices) {
-        return addDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
-            return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d);
-        }, capturePreset, role, devices);
-    }
-
-    private int removeDevicesRoleForCapturePreset(int capturePreset, int role,
-                                                  @NonNull List<AudioDeviceAttributes> devices) {
-        return removeDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
-            return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d);
-        }, capturePreset, role, devices);
-    }
-
-    private int setDevicesRoleForCapturePreset(int capturePreset, int role,
-                                               @NonNull List<AudioDeviceAttributes> devices) {
-        return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
-            return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d);
-        }, (p, r, d) -> {
-                return mAudioSystem.clearDevicesRoleForCapturePreset(p, r);
-            }, capturePreset, role, devices);
-    }
-
-    private int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
-        return clearDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
-            return mAudioSystem.clearDevicesRoleForCapturePreset(p, r);
-        }, capturePreset, role);
-    }
-
     /*package*/ void registerCapturePresetDevicesRoleDispatcher(
             @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
         mDevRoleCapturePresetDispatchers.register(dispatcher);
@@ -945,208 +849,7 @@
         mDevRoleCapturePresetDispatchers.unregister(dispatcher);
     }
 
-    private int addDevicesRoleForStrategy(int strategy, int role,
-                                          @NonNull List<AudioDeviceAttributes> devices) {
-        return addDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
-            return mAudioSystem.setDevicesRoleForStrategy(s, r, d);
-        }, strategy, role, devices);
-    }
-
-    private int removeDevicesRoleForStrategy(int strategy, int role,
-                                      @NonNull List<AudioDeviceAttributes> devices) {
-        return removeDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
-            return mAudioSystem.removeDevicesRoleForStrategy(s, r, d);
-        }, strategy, role, devices);
-    }
-
-    private int setDevicesRoleForStrategy(int strategy, int role,
-                                          @NonNull List<AudioDeviceAttributes> devices) {
-        return setDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
-            return mAudioSystem.setDevicesRoleForStrategy(s, r, d);
-        }, (s, r, d) -> {
-                return mAudioSystem.clearDevicesRoleForStrategy(s, r);
-            }, strategy, role, devices);
-    }
-
-    private int clearDevicesRoleForStrategy(int strategy, int role) {
-        return clearDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
-            return mAudioSystem.clearDevicesRoleForStrategy(s, r);
-        }, strategy, role);
-    }
-
-    //------------------------------------------------------------
-    // Cache for applied roles for strategies and devices. The cache avoids reapplying the
-    // same list of devices for a given role and strategy and the corresponding systematic
-    // redundant work in audio policy manager and audio flinger.
-    // The key is the pair <Strategy , Role> and the value is the current list of devices.
-
-    private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>>
-            mAppliedStrategyRoles = new ArrayMap<>();
-
-    // Cache for applied roles for capture presets and devices. The cache avoids reapplying the
-    // same list of devices for a given role and capture preset and the corresponding systematic
-    // redundant work in audio policy manager and audio flinger.
-    // The key is the pair <Preset , Role> and the value is the current list of devices.
-    private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>>
-            mAppliedPresetRoles = new ArrayMap<>();
-
-    interface AudioSystemInterface {
-        int deviceRoleAction(int usecase, int role, @Nullable List<AudioDeviceAttributes> devices);
-    }
-
-    private int addDevicesRole(
-            ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
-            AudioSystemInterface asi,
-            int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) {
-        synchronized (rolesMap) {
-            Pair<Integer, Integer> key = new Pair<>(useCase, role);
-            List<AudioDeviceAttributes> roleDevices = new ArrayList<>();
-            List<AudioDeviceAttributes> appliedDevices = new ArrayList<>();
-
-            if (rolesMap.containsKey(key)) {
-                roleDevices = rolesMap.get(key);
-                for (AudioDeviceAttributes device : devices) {
-                    if (!roleDevices.contains(device)) {
-                        appliedDevices.add(device);
-                    }
-                }
-            } else {
-                appliedDevices.addAll(devices);
-            }
-            if (appliedDevices.isEmpty()) {
-                return AudioSystem.SUCCESS;
-            }
-            final int status = asi.deviceRoleAction(useCase, role, appliedDevices);
-            if (status == AudioSystem.SUCCESS) {
-                roleDevices.addAll(appliedDevices);
-                rolesMap.put(key, roleDevices);
-            }
-            return status;
-        }
-    }
-
-    private int removeDevicesRole(
-            ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
-            AudioSystemInterface asi,
-            int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) {
-        synchronized (rolesMap) {
-            Pair<Integer, Integer> key = new Pair<>(useCase, role);
-            if (!rolesMap.containsKey(key)) {
-                return AudioSystem.SUCCESS;
-            }
-            List<AudioDeviceAttributes> roleDevices = rolesMap.get(key);
-            List<AudioDeviceAttributes> appliedDevices = new ArrayList<>();
-            for (AudioDeviceAttributes device : devices) {
-                if (roleDevices.contains(device)) {
-                    appliedDevices.add(device);
-                }
-            }
-            if (appliedDevices.isEmpty()) {
-                return AudioSystem.SUCCESS;
-            }
-            final int status = asi.deviceRoleAction(useCase, role, appliedDevices);
-            if (status == AudioSystem.SUCCESS) {
-                roleDevices.removeAll(appliedDevices);
-                if (roleDevices.isEmpty()) {
-                    rolesMap.remove(key);
-                } else {
-                    rolesMap.put(key, roleDevices);
-                }
-            }
-            return status;
-        }
-    }
-
-    private int setDevicesRole(
-            ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
-            AudioSystemInterface addOp,
-            AudioSystemInterface clearOp,
-            int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) {
-        synchronized (rolesMap) {
-            Pair<Integer, Integer> key = new Pair<>(useCase, role);
-            List<AudioDeviceAttributes> roleDevices = new ArrayList<>();
-            List<AudioDeviceAttributes> appliedDevices = new ArrayList<>();
-
-            if (rolesMap.containsKey(key)) {
-                roleDevices = rolesMap.get(key);
-                boolean equal = false;
-                if (roleDevices.size() == devices.size()) {
-                    roleDevices.retainAll(devices);
-                    equal = roleDevices.size() == devices.size();
-                }
-                if (!equal) {
-                    clearOp.deviceRoleAction(useCase, role, null);
-                    roleDevices.clear();
-                    appliedDevices.addAll(devices);
-                }
-            } else {
-                appliedDevices.addAll(devices);
-            }
-            if (appliedDevices.isEmpty()) {
-                return AudioSystem.SUCCESS;
-            }
-            final int status = addOp.deviceRoleAction(useCase, role, appliedDevices);
-            if (status == AudioSystem.SUCCESS) {
-                roleDevices.addAll(appliedDevices);
-                rolesMap.put(key, roleDevices);
-            }
-            return status;
-        }
-    }
-
-    private int clearDevicesRole(
-            ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
-            AudioSystemInterface asi, int useCase, int role) {
-        synchronized (rolesMap) {
-            Pair<Integer, Integer> key = new Pair<>(useCase, role);
-            if (!rolesMap.containsKey(key)) {
-                return AudioSystem.SUCCESS;
-            }
-            final int status = asi.deviceRoleAction(useCase, role, null);
-            if (status == AudioSystem.SUCCESS) {
-                rolesMap.remove(key);
-            }
-            return status;
-        }
-    }
-
-    @GuardedBy("mDevicesLock")
-    private void purgeDevicesRoles_l() {
-        purgeRoles(mAppliedStrategyRoles, (s, r, d) -> {
-            return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); });
-        purgeRoles(mAppliedPresetRoles, (p, r, d) -> {
-            return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); });
-    }
-
-    @GuardedBy("mDevicesLock")
-    private void purgeRoles(
-            ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
-            AudioSystemInterface asi) {
-        synchronized (rolesMap) {
-            Iterator<Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole =
-                    rolesMap.entrySet().iterator();
-            while (itRole.hasNext()) {
-                Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry =
-                        itRole.next();
-                Pair<Integer, Integer> keyRole = entry.getKey();
-                Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator();
-                while (itDev.hasNext()) {
-                    AudioDeviceAttributes ada = itDev.next();
-                    final String devKey = DeviceInfo.makeDeviceListKey(ada.getInternalType(),
-                            ada.getAddress());
-                    if (mConnectedDevices.get(devKey) == null) {
-                        asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada));
-                        itDev.remove();
-                    }
-                }
-                if (rolesMap.get(keyRole).isEmpty()) {
-                    itRole.remove();
-                }
-            }
-        }
-    }
-
-//-----------------------------------------------------------------------
+    //-----------------------------------------------------------------------
 
     /**
      * Check if a device is in the list of connected devices
@@ -1168,11 +871,10 @@
      * @param connect true if connection
      * @param isForTesting if true, not calling AudioSystem for the connection as this is
      *                    just for testing
-     * @param btDevice the corresponding Bluetooth device when relevant.
      * @return false if an error was reported by AudioSystem
      */
     /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
-            boolean isForTesting, @Nullable BluetoothDevice btDevice) {
+            boolean isForTesting) {
         int device = attributes.getInternalType();
         String address = attributes.getAddress();
         String deviceName = attributes.getName();
@@ -1187,7 +889,6 @@
                 .set(MediaMetrics.Property.MODE, connect
                         ? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT)
                 .set(MediaMetrics.Property.NAME, deviceName);
-        boolean status = false;
         synchronized (mDevicesLock) {
             final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
             if (AudioService.DEBUG_DEVICES) {
@@ -1215,33 +916,24 @@
                             .record();
                     return false;
                 }
-                mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
+                mConnectedDevices.put(deviceKey, new DeviceInfo(
+                        device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
                 mDeviceBroker.postAccessoryPlugMediaUnmute(device);
-                status = true;
+                mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
+                return true;
             } else if (!connect && isConnected) {
                 mAudioSystem.setDeviceConnectionState(attributes,
                         AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
                 // always remove even if disconnection failed
                 mConnectedDevices.remove(deviceKey);
-                status = true;
-            }
-            if (status) {
-                if (AudioSystem.isBluetoothScoDevice(device)) {
-                    updateBluetoothPreferredModes_l();
-                    if (connect) {
-                        mDeviceBroker.postNotifyPreferredAudioProfileApplied(btDevice);
-                    } else {
-                        purgeDevicesRoles_l();
-                    }
-                }
                 mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
-            } else {
-                Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
-                        + ", deviceSpec=" + di + ", connect=" + connect);
-                mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
+                return true;
             }
+            Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
+                    + ", deviceSpec=" + di + ", connect=" + connect);
         }
-        return status;
+        mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
+        return false;
     }
 
 
@@ -1450,20 +1142,15 @@
     // Internal utilities
 
     @GuardedBy("mDevicesLock")
-    private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo,
-                                         String eventSource) {
-        final String address = btInfo.mDevice.getAddress();
-        final String name = BtHelper.getName(btInfo.mDevice);
-        final int a2dpCodec = btInfo.mCodec;
-
+    private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
+            int a2dpCodec) {
         // enable A2DP before notifying A2DP connection to avoid unnecessary processing in
         // audio policy manager
         mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
         // at this point there could be another A2DP device already connected in APM, but it
         // doesn't matter as this new one will overwrite the previous one
-        AudioDeviceAttributes ada = new AudioDeviceAttributes(
-                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
-        final int res = mAudioSystem.setDeviceConnectionState(ada,
+        final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
                 AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
 
         // TODO: log in MediaMetrics once distinction between connection failure and
@@ -1485,7 +1172,8 @@
         // The convention for head tracking sensors associated with A2DP devices is to
         // use a UUID derived from the MAC address as follows:
         //   time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
-        UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
+        UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
+                new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
                 address, a2dpCodec, sensorUuid);
         final String diKey = di.getKey();
@@ -1496,206 +1184,6 @@
 
         mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
         setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
-
-        updateBluetoothPreferredModes_l();
-        mDeviceBroker.postNotifyPreferredAudioProfileApplied(btInfo.mDevice);
-    }
-
-    static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER,
-            AudioSource.VOICE_RECOGNITION, AudioSource.VOICE_COMMUNICATION,
-            AudioSource.UNPROCESSED, AudioSource.VOICE_PERFORMANCE, AudioSource.HOTWORD};
-
-    // reflects system property persist.bluetooth.enable_dual_mode_audio
-    final boolean mBluetoothDualModeEnabled;
-    /**
-     * Goes over all connected Bluetooth devices and set the audio policy device role to DISABLED
-     * or not according to their own and other devices modes.
-     * The top priority is given to LE devices, then SCO ,then A2DP.
-     */
-    @GuardedBy("mDevicesLock")
-    private void applyConnectedDevicesRoles_l() {
-        if (!mBluetoothDualModeEnabled) {
-            return;
-        }
-        DeviceInfo leOutDevice =
-                getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
-        DeviceInfo leInDevice =
-                getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET);
-        DeviceInfo a2dpDevice =
-                getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
-        DeviceInfo scoOutDevice =
-                getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET);
-        DeviceInfo scoInDevice =
-                getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET);
-        boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled());
-        boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled())
-                || (leInDevice != null && leInDevice.isDuplexModeEnabled());
-        AudioDeviceAttributes communicationDevice =
-                mDeviceBroker.mActiveCommunicationDevice == null
-                        ? null : ((mDeviceBroker.isInCommunication()
-                                    && mDeviceBroker.mActiveCommunicationDevice != null)
-                            ? new AudioDeviceAttributes(mDeviceBroker.mActiveCommunicationDevice)
-                            : null);
-
-        if (AudioService.DEBUG_DEVICES) {
-            Log.i(TAG, "applyConnectedDevicesRoles_l\n - leOutDevice: " + leOutDevice
-                    + "\n - leInDevice: " + leInDevice
-                    + "\n - a2dpDevice: " + a2dpDevice
-                    + "\n - scoOutDevice: " + scoOutDevice
-                    + "\n - scoInDevice: " + scoInDevice
-                    + "\n - disableA2dp: " + disableA2dp
-                    + ", disableSco: " + disableSco);
-        }
-
-        for (DeviceInfo di : mConnectedDevices.values()) {
-            if (!AudioSystem.isBluetoothDevice(di.mDeviceType)) {
-                continue;
-            }
-            AudioDeviceAttributes ada =
-                    new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
-            if (AudioService.DEBUG_DEVICES) {
-                Log.i(TAG, "  + checking Device: " + ada);
-            }
-            if (ada.equalTypeAddress(communicationDevice)) {
-                continue;
-            }
-
-            if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) {
-                for (AudioProductStrategy strategy : mStrategies) {
-                    boolean disable = false;
-                    if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) {
-                        if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
-                            disable = disableSco || !di.isDuplexModeEnabled();
-                        } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) {
-                            disable = !di.isDuplexModeEnabled();
-                        }
-                    } else {
-                        if (AudioSystem.isBluetoothA2dpOutDevice(di.mDeviceType)) {
-                            disable = disableA2dp || !di.isOutputOnlyModeEnabled();
-                        } else if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
-                            disable = disableSco || !di.isOutputOnlyModeEnabled();
-                        } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) {
-                            disable = !di.isOutputOnlyModeEnabled();
-                        }
-                    }
-                    if (AudioService.DEBUG_DEVICES) {
-                        Log.i(TAG, "     - strategy: " + strategy.getId()
-                                + ", disable: " + disable);
-                    }
-                    if (disable) {
-                        addDevicesRoleForStrategy(strategy.getId(),
-                                AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
-                    } else {
-                        removeDevicesRoleForStrategy(strategy.getId(),
-                                AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
-                    }
-                }
-            }
-            if (AudioSystem.isBluetoothInDevice(di.mDeviceType)) {
-                for (int capturePreset : CAPTURE_PRESETS) {
-                    boolean disable = false;
-                    if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
-                        disable = disableSco || !di.isDuplexModeEnabled();
-                    } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) {
-                        disable = !di.isDuplexModeEnabled();
-                    }
-                    if (AudioService.DEBUG_DEVICES) {
-                        Log.i(TAG, "      - capturePreset: " + capturePreset
-                                + ", disable: " + disable);
-                    }
-                    if (disable) {
-                        addDevicesRoleForCapturePreset(capturePreset,
-                                AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
-                    } else {
-                        removeDevicesRoleForCapturePreset(capturePreset,
-                                AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
-                    }
-                }
-            }
-        }
-    }
-
-    /* package */ void applyConnectedDevicesRoles() {
-        synchronized (mDevicesLock) {
-            applyConnectedDevicesRoles_l();
-        }
-    }
-
-    @GuardedBy("mDevicesLock")
-    int checkProfileIsConnected(int profile) {
-        switch (profile) {
-            case BluetoothProfile.HEADSET:
-                if (getFirstConnectedDeviceOfTypes(
-                        AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null
-                        || getFirstConnectedDeviceOfTypes(
-                                AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) {
-                    return profile;
-                }
-                break;
-            case BluetoothProfile.A2DP:
-                if (getFirstConnectedDeviceOfTypes(
-                        AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) {
-                    return profile;
-                }
-                break;
-            case BluetoothProfile.LE_AUDIO:
-            case BluetoothProfile.LE_AUDIO_BROADCAST:
-                if (getFirstConnectedDeviceOfTypes(
-                        AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null
-                        || getFirstConnectedDeviceOfTypes(
-                                AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) {
-                    return profile;
-                }
-                break;
-            default:
-                break;
-        }
-        return 0;
-    }
-
-    @GuardedBy("mDevicesLock")
-    private void updateBluetoothPreferredModes_l() {
-        if (!mBluetoothDualModeEnabled) {
-            return;
-        }
-        HashSet<String> processedAddresses = new HashSet<>(0);
-        for (DeviceInfo di : mConnectedDevices.values()) {
-            if (!AudioSystem.isBluetoothDevice(di.mDeviceType)
-                    || processedAddresses.contains(di.mDeviceAddress)) {
-                continue;
-            }
-            Bundle preferredProfiles = BtHelper.getPreferredAudioProfiles(di.mDeviceAddress);
-            if (AudioService.DEBUG_DEVICES) {
-                Log.i(TAG, "updateBluetoothPreferredModes_l processing device address: "
-                        + di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles);
-            }
-            for (DeviceInfo di2 : mConnectedDevices.values()) {
-                if (!AudioSystem.isBluetoothDevice(di2.mDeviceType)
-                        || !di.mDeviceAddress.equals(di2.mDeviceAddress)) {
-                    continue;
-                }
-                int profile = BtHelper.getProfileFromType(di2.mDeviceType);
-                if (profile == 0) {
-                    continue;
-                }
-                int preferredProfile = checkProfileIsConnected(
-                        preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
-                if (preferredProfile == profile || preferredProfile == 0) {
-                    di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
-                } else {
-                    di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
-                }
-                preferredProfile = checkProfileIsConnected(
-                        preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
-                if (preferredProfile == profile || preferredProfile == 0) {
-                    di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY);
-                } else {
-                    di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY);
-                }
-            }
-            processedAddresses.add(di.mDeviceAddress);
-        }
-        applyConnectedDevicesRoles_l();
     }
 
     @GuardedBy("mDevicesLock")
@@ -1743,9 +1231,6 @@
         // Remove A2DP routes as well
         setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
         mmi.record();
-
-        updateBluetoothPreferredModes_l();
-        purgeDevicesRoles_l();
     }
 
     @GuardedBy("mDevicesLock")
@@ -1775,7 +1260,8 @@
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
         mConnectedDevices.put(
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
-                new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
+                new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "",
+                        address, AudioSystem.AUDIO_FORMAT_DEFAULT));
     }
 
     @GuardedBy("mDevicesLock")
@@ -1801,7 +1287,8 @@
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
         mConnectedDevices.put(
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
-                new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address));
+                new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name,
+                        address, AudioSystem.AUDIO_FORMAT_DEFAULT));
         mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
         mDeviceBroker.postApplyVolumeOnDevice(streamType,
                 AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
@@ -1839,56 +1326,29 @@
      * @return true if a DEVICE_OUT_HEARING_AID is connected, false otherwise.
      */
     boolean isHearingAidConnected() {
-        return getFirstConnectedDeviceOfTypes(
-                Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null;
-    }
-
-    /**
-     * Returns a DeviceInfo for the fist connected device matching one of the supplied types
-     */
-    private DeviceInfo getFirstConnectedDeviceOfTypes(Set<Integer> internalTypes) {
-        List<DeviceInfo> devices = getConnectedDevicesOfTypes(internalTypes);
-        return devices.isEmpty() ? null : devices.get(0);
-    }
-
-    /**
-     * Returns a list of connected devices matching one one of the supplied types
-     */
-    private List<DeviceInfo> getConnectedDevicesOfTypes(Set<Integer> internalTypes) {
-        ArrayList<DeviceInfo> devices = new ArrayList<>();
         synchronized (mDevicesLock) {
             for (DeviceInfo di : mConnectedDevices.values()) {
-                if (internalTypes.contains(di.mDeviceType)) {
-                    devices.add(di);
+                if (di.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
+                    return true;
                 }
             }
+            return false;
         }
-        return devices;
-    }
-
-    /* package */ AudioDeviceAttributes getDeviceOfType(int type) {
-        DeviceInfo di = getFirstConnectedDeviceOfTypes(Sets.newHashSet(type));
-        return di == null ? null : new AudioDeviceAttributes(
-                    di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
     }
 
     @GuardedBy("mDevicesLock")
-    private void makeLeAudioDeviceAvailable(
-            AudioDeviceBroker.BtDeviceInfo btInfo, int streamType, String eventSource) {
-        final String address = btInfo.mDevice.getAddress();
-        final String name = BtHelper.getName(btInfo.mDevice);
-        final int volumeIndex = btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10;
-        final int device = btInfo.mAudioSystemDevice;
-
+    private void makeLeAudioDeviceAvailable(String address, String name, int streamType,
+            int volumeIndex, int device, String eventSource) {
         if (device != AudioSystem.DEVICE_NONE) {
             /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
              * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
              */
             mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
 
-            AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
-            final int res = AudioSystem.setDeviceConnectionState(ada,
-                    AudioSystem.DEVICE_STATE_AVAILABLE,  AudioSystem.AUDIO_FORMAT_DEFAULT);
+            final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                    device, address, name),
+                    AudioSystem.DEVICE_STATE_AVAILABLE,
+                    AudioSystem.AUDIO_FORMAT_DEFAULT);
             if (res != AudioSystem.AUDIO_STATUS_OK) {
                 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "APM failed to make available LE Audio device addr=" + address
@@ -1899,13 +1359,12 @@
                 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "LE Audio device addr=" + address + " now available").printLog(TAG));
             }
+
             // Reset LEA suspend state each time a new sink is connected
             mDeviceBroker.clearLeAudioSuspended();
 
-            UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
-                    new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT,
-                            sensorUuid));
+                    new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
             mDeviceBroker.postAccessoryPlugMediaUnmute(device);
             setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
         }
@@ -1921,9 +1380,6 @@
         final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
         mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
         mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
-
-        updateBluetoothPreferredModes_l();
-        mDeviceBroker.postNotifyPreferredAudioProfileApplied(btInfo.mDevice);
     }
 
     @GuardedBy("mDevicesLock")
@@ -1948,9 +1404,6 @@
         }
 
         setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
-
-        updateBluetoothPreferredModes_l();
-        purgeDevicesRoles_l();
     }
 
     @GuardedBy("mDevicesLock")
@@ -2286,6 +1739,18 @@
         }
     }
 
+    /* package */ AudioDeviceAttributes getDeviceOfType(int type) {
+        synchronized (mDevicesLock) {
+            for (DeviceInfo di : mConnectedDevices.values()) {
+                if (di.mDeviceType == type) {
+                    return new AudioDeviceAttributes(
+                            di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
+                }
+            }
+        }
+        return null;
+    }
+
     //----------------------------------------------------------
     // For tests only
 
@@ -2296,12 +1761,10 @@
      */
     @VisibleForTesting
     public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
-        for (DeviceInfo di : getConnectedDevicesOfTypes(
-                Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
-            if (di.mDeviceAddress.equals(device.getAddress())) {
-                return true;
-            }
+        final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                device.getAddress());
+        synchronized (mDevicesLock) {
+            return (mConnectedDevices.get(key) != null);
         }
-        return false;
     }
 }
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 1142a8d..7af7ed5 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -435,7 +435,7 @@
     }
 
     /**
-     * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, List)}
+     * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, int[], String[])}
      * @param capturePreset
      * @param role
      * @param devicesToRemove
@@ -448,19 +448,6 @@
     }
 
     /**
-     * Same as {@link AudioSystem#addDevicesRoleForCapturePreset(int, int, List)}
-     * @param capturePreset the capture preset to configure
-     * @param role the role of the devices
-     * @param devices the list of devices to be added as role for the given capture preset
-     * @return {@link #SUCCESS} if successfully add
-     */
-    public int addDevicesRoleForCapturePreset(
-            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
-        invalidateRoutingCache();
-        return AudioSystem.addDevicesRoleForCapturePreset(capturePreset, role, devices);
-    }
-
-    /**
      * Same as {@link AudioSystem#}
      * @param capturePreset
      * @param role
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index e46c3cc..8c27c3e 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -33,7 +33,6 @@
 import android.media.AudioSystem;
 import android.media.BluetoothProfileConnectionInfo;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -151,12 +150,60 @@
         }
     }
 
+    //----------------------------------------------------------------------
+    /*package*/ static class BluetoothA2dpDeviceInfo {
+        private final @NonNull BluetoothDevice mBtDevice;
+        private final int mVolume;
+        private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
+
+        BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) {
+            this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT);
+        }
+
+        BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) {
+            mBtDevice = btDevice;
+            mVolume = volume;
+            mCodec = codec;
+        }
+
+        public @NonNull BluetoothDevice getBtDevice() {
+            return mBtDevice;
+        }
+
+        public int getVolume() {
+            return mVolume;
+        }
+
+        public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() {
+            return mCodec;
+        }
+
+        // redefine equality op so we can match messages intended for this device
+        @Override
+        public boolean equals(Object o) {
+            if (o == null) {
+                return false;
+            }
+            if (this == o) {
+                return true;
+            }
+            if (o instanceof BluetoothA2dpDeviceInfo) {
+                return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice());
+            }
+            return false;
+        }
+
+
+    }
+
     // A2DP device events
     /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0;
+    /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1;
 
-    /*package*/ static String deviceEventToString(int event) {
+    /*package*/ static String a2dpDeviceEventToString(int event) {
         switch (event) {
             case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE";
+            case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE";
             default:
                 return new String("invalid event:" + event);
         }
@@ -573,12 +620,11 @@
         return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
     }
 
-    private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
+    private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
         if (btDevice == null) {
             return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
         }
         String address = btDevice.getAddress();
-        String name = getName(btDevice);
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
@@ -600,7 +646,7 @@
                     + " btClass: " + (btClass == null ? "Unknown" : btClass)
                     + " nativeType: " + nativeType + " address: " + address);
         }
-        return new AudioDeviceAttributes(nativeType, address, name);
+        return new AudioDeviceAttributes(nativeType, address);
     }
 
     private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
@@ -609,9 +655,12 @@
         }
         int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
         AudioDeviceAttributes audioDevice =  btHeadsetDeviceToAudioDevice(btDevice);
+        String btDeviceName =  getName(btDevice);
         boolean result = false;
         if (isActive) {
-            result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
+            result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
+                    audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName),
+                    isActive);
         } else {
             int[] outDeviceTypes = {
                     AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
@@ -620,14 +669,14 @@
             };
             for (int outDeviceType : outDeviceTypes) {
                 result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
-                        outDeviceType, audioDevice.getAddress(), audioDevice.getName()),
-                        isActive, btDevice);
+                        outDeviceType, audioDevice.getAddress(), btDeviceName),
+                        isActive);
             }
         }
         // handleDeviceConnection() && result to make sure the method get executed
         result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
-                        inDevice, audioDevice.getAddress(), audioDevice.getName()),
-                isActive, btDevice) && result;
+                        inDevice, audioDevice.getAddress(), btDeviceName),
+                isActive) && result;
         return result;
     }
 
@@ -924,30 +973,6 @@
         }
     }
 
-    /*package */ static int getProfileFromType(int deviceType) {
-        if (AudioSystem.isBluetoothA2dpOutDevice(deviceType)) {
-            return BluetoothProfile.A2DP;
-        } else if (AudioSystem.isBluetoothScoDevice(deviceType)) {
-            return BluetoothProfile.HEADSET;
-        } else if (AudioSystem.isBluetoothLeDevice(deviceType)) {
-            return BluetoothProfile.LE_AUDIO;
-        }
-        return 0; // 0 is not a valid profile
-    }
-
-    /*package */ static Bundle getPreferredAudioProfiles(String address) {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address));
-    }
-
-    /**
-     * Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices
-     * have been applied.
-     */
-    public static void onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) {
-        BluetoothAdapter.getDefaultAdapter().notifyActiveDeviceChangeApplied(btDevice);
-    }
-
     /**
      * Returns the string equivalent for the btDeviceClass class.
      */
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 31f0c05..a57dd40 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -303,8 +303,9 @@
                 SAFE_MEDIA_VOLUME_UNINITIALIZED);
         mSafeMediaVolumeDevices.append(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
                 SAFE_MEDIA_VOLUME_UNINITIALIZED);
-        mSafeMediaVolumeDevices.append(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                SAFE_MEDIA_VOLUME_UNINITIALIZED);
+        // TODO(b/278265907): enable A2DP when we can distinguish A2DP headsets
+        // mSafeMediaVolumeDevices.append(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+        //        SAFE_MEDIA_VOLUME_UNINITIALIZED);
     }
 
     float getOutputRs2UpperBound() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
index a48a9d1..cd2a26f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
@@ -37,13 +37,17 @@
      */
     static final int AUTHENTICATOR_DEFAULT = 0;
     /**
-     * Indicated this authenticator has received a lockout.
+     * Indicated this authenticator has received a permanent lockout.
      */
-    static final int AUTHENTICATOR_LOCKED = 1 << 0;
+    static final int AUTHENTICATOR_PERMANENT_LOCKED = 1 << 0;
+    /**
+     * Indicates this authenticator has received a timed unlock.
+     */
+    static final int AUTHENTICATOR_TIMED_LOCKED = 1 << 1;
     /**
      * Indicates this authenticator has received a successful unlock.
      */
-    static final int AUTHENTICATOR_UNLOCKED = 1 << 1;
+    static final int AUTHENTICATOR_UNLOCKED = 1 << 2;
     private static final String TAG = "AuthResultCoordinator";
     private final Map<Integer, Integer> mAuthenticatorState;
 
@@ -85,7 +89,14 @@
      * Adds a lock out of a given strength to the current operation list.
      */
     void lockedOutFor(@Authenticators.Types int strength) {
-        updateState(strength, (old) -> AUTHENTICATOR_LOCKED | old);
+        updateState(strength, (old) -> AUTHENTICATOR_PERMANENT_LOCKED | old);
+    }
+
+    /**
+     * Adds a timed lock out of a given strength to the current operation list.
+     */
+    void lockOutTimed(@Authenticators.Types int strength) {
+        updateState(strength, (old) -> AUTHENTICATOR_TIMED_LOCKED | old);
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
index 1aee5d4..2653ce7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
@@ -16,21 +16,19 @@
 
 package com.android.server.biometrics.sensors;
 
-import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_PERMANENT_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_TIMED_LOCKED;
 import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_UNLOCKED;
 
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.os.SystemClock;
-import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.time.Clock;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -45,9 +43,7 @@
 
     private final Set<Integer> mAuthOperations;
     private final MultiBiometricLockoutState mMultiBiometricLockoutState;
-    private final List<Pair<Integer, Long>> mTimedLockouts;
     private final RingBuffer mRingBuffer;
-    private final Clock mClock;
 
     private int mUserId;
     private boolean mIsAuthenticating;
@@ -63,8 +59,6 @@
         mAuthResultCoordinator = new AuthResultCoordinator();
         mMultiBiometricLockoutState = new MultiBiometricLockoutState(clock);
         mRingBuffer = new RingBuffer(100);
-        mTimedLockouts = new ArrayList<>();
-        mClock = clock;
     }
 
     /**
@@ -74,8 +68,6 @@
         mAuthOperations.clear();
         mUserId = userId;
         mIsAuthenticating = true;
-        mAuthOperations.clear();
-        mTimedLockouts.clear();
         mAuthResultCoordinator = new AuthResultCoordinator();
         mRingBuffer.addApiCall("internal : onAuthSessionStarted(" + userId + ")");
     }
@@ -85,39 +77,30 @@
      *
      * This can happen two ways.
      * 1. Manually calling this API
-     * 2. If authStartedFor() was called, and all authentication attempts finish.
+     * 2. If authStartedFor() was called, and any authentication attempts finish.
      */
     void endAuthSession() {
-        if (mIsAuthenticating) {
-            final long currentTime = mClock.millis();
-            for (Pair<Integer, Long> timedLockouts : mTimedLockouts) {
-                mMultiBiometricLockoutState.increaseLockoutTime(mUserId, timedLockouts.first,
-                        timedLockouts.second + currentTime);
+        // User unlocks can also unlock timed lockout Authenticator.Types
+        final Map<Integer, Integer> result = mAuthResultCoordinator.getResult();
+        for (int authenticator : Arrays.asList(Authenticators.BIOMETRIC_CONVENIENCE,
+                Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG)) {
+            final Integer value = result.get(authenticator);
+            if ((value & AUTHENTICATOR_UNLOCKED) == AUTHENTICATOR_UNLOCKED) {
+                mMultiBiometricLockoutState.clearPermanentLockOut(mUserId, authenticator);
+                mMultiBiometricLockoutState.clearTimedLockout(mUserId, authenticator);
+            } else if ((value & AUTHENTICATOR_PERMANENT_LOCKED) == AUTHENTICATOR_PERMANENT_LOCKED) {
+                mMultiBiometricLockoutState.setPermanentLockOut(mUserId, authenticator);
+            } else if ((value & AUTHENTICATOR_TIMED_LOCKED) == AUTHENTICATOR_TIMED_LOCKED) {
+                mMultiBiometricLockoutState.setTimedLockout(mUserId, authenticator);
             }
-            // User unlocks can also unlock timed lockout Authenticator.Types
-            final Map<Integer, Integer> result = mAuthResultCoordinator.getResult();
-            for (int authenticator : Arrays.asList(Authenticators.BIOMETRIC_CONVENIENCE,
-                    Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG)) {
-                final Integer value = result.get(authenticator);
-                if ((value & AUTHENTICATOR_UNLOCKED) == AUTHENTICATOR_UNLOCKED) {
-                    mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator,
-                            true /* canAuthenticate */);
-                    mMultiBiometricLockoutState.clearLockoutTime(mUserId, authenticator);
-                } else if ((value & AUTHENTICATOR_LOCKED) == AUTHENTICATOR_LOCKED) {
-                    mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator,
-                            false /* canAuthenticate */);
-                }
-
-            }
-
-            mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
-            clearSession();
         }
+
+        mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
+        clearSession();
     }
 
     private void clearSession() {
         mIsAuthenticating = false;
-        mTimedLockouts.clear();
         mAuthOperations.clear();
     }
 
@@ -171,7 +154,7 @@
                         + ", sensorId=" + sensorId + "time=" + time + ", requestId=" + requestId
                         + ")";
         mRingBuffer.addApiCall(lockedOutStr);
-        mTimedLockouts.add(new Pair<>(biometricStrength, time));
+        mAuthResultCoordinator.lockOutTimed(biometricStrength);
         attemptToFinish(userId, sensorId, lockedOutStr);
     }
 
@@ -202,9 +185,8 @@
             // Lockouts cannot be reset by non-strong biometrics
             return;
         }
-        mMultiBiometricLockoutState.setAuthenticatorTo(userId, biometricStrength,
-                true /*canAuthenticate */);
-        mMultiBiometricLockoutState.clearLockoutTime(userId, biometricStrength);
+        mMultiBiometricLockoutState.clearPermanentLockOut(userId, biometricStrength);
+        mMultiBiometricLockoutState.clearTimedLockout(userId, biometricStrength);
     }
 
     private void attemptToFinish(int userId, int sensorId, String description) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
index c24a989..45933fe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
@@ -50,10 +50,11 @@
     private Map<Integer, AuthenticatorState> createUnlockedMap() {
         Map<Integer, AuthenticatorState> lockOutMap = new HashMap<>();
         lockOutMap.put(BIOMETRIC_STRONG,
-                new AuthenticatorState(BIOMETRIC_STRONG, false, 0, mClock));
-        lockOutMap.put(BIOMETRIC_WEAK, new AuthenticatorState(BIOMETRIC_WEAK, false, 0, mClock));
+                new AuthenticatorState(BIOMETRIC_STRONG, false, false));
+        lockOutMap.put(BIOMETRIC_WEAK,
+                new AuthenticatorState(BIOMETRIC_WEAK, false, false));
         lockOutMap.put(BIOMETRIC_CONVENIENCE,
-                new AuthenticatorState(BIOMETRIC_CONVENIENCE, false, 0, mClock));
+                new AuthenticatorState(BIOMETRIC_CONVENIENCE, false, false));
         return lockOutMap;
     }
 
@@ -64,54 +65,71 @@
         return mCanUserAuthenticate.get(userId);
     }
 
-    void setAuthenticatorTo(int userId, @Authenticators.Types int strength, boolean canAuth) {
+    void setPermanentLockOut(int userId, @Authenticators.Types int strength) {
         final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
         switch (strength) {
             case Authenticators.BIOMETRIC_STRONG:
-                authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = !canAuth;
+                authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = true;
                 // fall through
             case Authenticators.BIOMETRIC_WEAK:
-                authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = !canAuth;
+                authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = true;
                 // fall through
             case Authenticators.BIOMETRIC_CONVENIENCE:
-                authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = !canAuth;
+                authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = true;
                 return;
             default:
                 Slog.e(TAG, "increaseLockoutTime called for invalid strength : "  + strength);
         }
     }
 
-    void increaseLockoutTime(int userId, @Authenticators.Types int strength, long duration) {
+    void clearPermanentLockOut(int userId, @Authenticators.Types int strength) {
         final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
         switch (strength) {
             case Authenticators.BIOMETRIC_STRONG:
-                authMap.get(BIOMETRIC_STRONG).increaseLockoutTo(duration);
+                authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = false;
                 // fall through
             case Authenticators.BIOMETRIC_WEAK:
-                authMap.get(BIOMETRIC_WEAK).increaseLockoutTo(duration);
+                authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = false;
                 // fall through
             case Authenticators.BIOMETRIC_CONVENIENCE:
-                authMap.get(BIOMETRIC_CONVENIENCE).increaseLockoutTo(duration);
+                authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = false;
                 return;
             default:
                 Slog.e(TAG, "increaseLockoutTime called for invalid strength : "  + strength);
         }
     }
 
-    void clearLockoutTime(int userId, @Authenticators.Types int strength) {
+    void setTimedLockout(int userId, @Authenticators.Types int strength) {
         final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
         switch (strength) {
             case Authenticators.BIOMETRIC_STRONG:
-                authMap.get(BIOMETRIC_STRONG).setTimedLockout(0);
+                authMap.get(BIOMETRIC_STRONG).mTimedLockout = true;
                 // fall through
             case Authenticators.BIOMETRIC_WEAK:
-                authMap.get(BIOMETRIC_WEAK).setTimedLockout(0);
+                authMap.get(BIOMETRIC_WEAK).mTimedLockout = true;
                 // fall through
             case Authenticators.BIOMETRIC_CONVENIENCE:
-                authMap.get(BIOMETRIC_CONVENIENCE).setTimedLockout(0);
+                authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = true;
                 return;
             default:
-                Slog.e(TAG, "clearLockoutTime called for invalid strength : "  + strength);
+                Slog.e(TAG, "increaseLockoutTime called for invalid strength : "  + strength);
+        }
+    }
+
+    void clearTimedLockout(int userId, @Authenticators.Types int strength) {
+        final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
+        switch (strength) {
+            case Authenticators.BIOMETRIC_STRONG:
+                authMap.get(BIOMETRIC_STRONG).mTimedLockout = false;
+                // fall through
+            case Authenticators.BIOMETRIC_WEAK:
+                authMap.get(BIOMETRIC_WEAK).mTimedLockout = false;
+                // fall through
+            case Authenticators.BIOMETRIC_CONVENIENCE:
+                authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = false;
+                return;
+            default:
+                Slog.e(TAG, "increaseLockoutTime called for invalid strength : "  + strength);
         }
     }
 
@@ -132,7 +150,7 @@
         final AuthenticatorState state = authMap.get(strength);
         if (state.mPermanentlyLockedOut) {
             return LockoutTracker.LOCKOUT_PERMANENT;
-        } else if (state.isTimedLockout()) {
+        } else if (state.mTimedLockout) {
             return LockoutTracker.LOCKOUT_TIMED;
         } else {
             return LockoutTracker.LOCKOUT_NONE;
@@ -158,43 +176,21 @@
     private static class AuthenticatorState {
         private Integer mAuthenticatorType;
         private boolean mPermanentlyLockedOut;
-        private long mTimedLockout;
-        private Clock mClock;
+        private boolean mTimedLockout;
 
         AuthenticatorState(Integer authenticatorId, boolean permanentlyLockedOut,
-                long timedLockout, Clock clock) {
+                boolean timedLockout) {
             mAuthenticatorType = authenticatorId;
             mPermanentlyLockedOut = permanentlyLockedOut;
             mTimedLockout = timedLockout;
-            mClock = clock;
-        }
-
-        boolean canAuthenticate() {
-            return !mPermanentlyLockedOut && !isTimedLockout();
-        }
-
-        boolean isTimedLockout() {
-            return mClock.millis() - mTimedLockout < 0;
-        }
-
-        void setTimedLockout(long duration) {
-            mTimedLockout = duration;
-        }
-
-        /**
-         * Either increases the lockout to duration, or leaves it as it, whichever is longer.
-         */
-        void increaseLockoutTo(long duration) {
-            mTimedLockout = Math.max(mTimedLockout, duration);
         }
 
         String toString(long currentTime) {
-            final String duration =
-                    mTimedLockout - currentTime > 0 ? (mTimedLockout - currentTime) + "ms" : "none";
+            final String timedLockout = mTimedLockout ? "true" : "false";
             final String permanentLockout = mPermanentlyLockedOut ? "true" : "false";
-            return String.format("(%s, permanentLockout=%s, timedLockoutRemaining=%s)",
+            return String.format("(%s, permanentLockout=%s, timedLockout=%s)",
                     BiometricManager.authenticatorToStr(mAuthenticatorType), permanentLockout,
-                    duration);
+                    timedLockout);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 759c52a..1a12fcd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -28,6 +28,7 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -88,10 +89,9 @@
 
     void onLockoutCleared() {
         resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
-                mLockoutResetDispatcher);
+                mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(),
+                mBiometricStrength, getRequestId());
         mCallback.onClientFinished(this, true /* success */);
-        getBiometricContext().getAuthSessionCoordinator()
-                .resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
     }
 
     public boolean interruptsPrecedingClients() {
@@ -108,7 +108,10 @@
      */
     static void resetLocalLockoutStateToNone(int sensorId, int userId,
             @NonNull LockoutCache lockoutTracker,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull AuthSessionCoordinator authSessionCoordinator,
+            @Authenticators.Types int biometricStrength, long requestId) {
+        authSessionCoordinator.resetLockoutFor(userId, biometricStrength, requestId);
         lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
         lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 0d30ddd..468bf55 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -54,6 +54,7 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -127,6 +128,9 @@
         private final LockoutCache mLockoutCache;
         @NonNull
         private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+        @NonNull
+        private AuthSessionCoordinator mAuthSessionCoordinator;
         @NonNull
         private final Callback mCallback;
 
@@ -134,6 +138,7 @@
                 @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
                 @NonNull LockoutCache lockoutTracker,
                 @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+                @NonNull AuthSessionCoordinator authSessionCoordinator,
                 @NonNull Callback callback) {
             mContext = context;
             mHandler = handler;
@@ -143,6 +148,7 @@
             mUserId = userId;
             mLockoutCache = lockoutTracker;
             mLockoutResetDispatcher = lockoutResetDispatcher;
+            mAuthSessionCoordinator = authSessionCoordinator;
             mCallback = callback;
         }
 
@@ -346,8 +352,12 @@
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
                 if (!(client instanceof FaceResetLockoutClient)) {
                     Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+                    // Given that onLockoutCleared() can happen at any time, and is not necessarily
+                    // coming from a specific client, set this to -1 to indicate it wasn't for a
+                    // specific request.
                     FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
-                            mLockoutCache, mLockoutResetDispatcher);
+                            mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+                            Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
                 } else {
                     Slog.d(mTag, "onLockoutCleared after resetLockout");
                     final FaceResetLockoutClient resetLockoutClient =
@@ -514,7 +524,8 @@
 
                         final HalSessionCallback resultController = new HalSessionCallback(mContext,
                                 mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
-                                lockoutResetDispatcher, () -> {
+                                lockoutResetDispatcher,
+                                biometricContext.getAuthSessionCoordinator(), () -> {
                             Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
                             mCurrentSession = null;
                         });
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 0b2421b..7a62034 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -28,6 +28,7 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -92,10 +93,9 @@
 
     void onLockoutCleared() {
         resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
-                mLockoutResetDispatcher);
+                mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(),
+                mBiometricStrength, getRequestId());
         mCallback.onClientFinished(this, true /* success */);
-        getBiometricContext().getAuthSessionCoordinator()
-                .resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
     }
 
     /**
@@ -108,9 +108,12 @@
      */
     static void resetLocalLockoutStateToNone(int sensorId, int userId,
             @NonNull LockoutCache lockoutTracker,
-            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull AuthSessionCoordinator authSessionCoordinator,
+            @Authenticators.Types int biometricStrength, long requestId) {
         lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
         lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
+        authSessionCoordinator.resetLockoutFor(userId, biometricStrength, requestId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 1dcf4e9..22ca816 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -52,6 +52,7 @@
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -131,12 +132,15 @@
         @NonNull
         private final LockoutResetDispatcher mLockoutResetDispatcher;
         @NonNull
+        private AuthSessionCoordinator mAuthSessionCoordinator;
+        @NonNull
         private final Callback mCallback;
 
         HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
                 @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
                 @NonNull LockoutCache lockoutTracker,
                 @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+                @NonNull AuthSessionCoordinator authSessionCoordinator,
                 @NonNull Callback callback) {
             mContext = context;
             mHandler = handler;
@@ -146,6 +150,7 @@
             mUserId = userId;
             mLockoutCache = lockoutTracker;
             mLockoutResetDispatcher = lockoutResetDispatcher;
+            mAuthSessionCoordinator = authSessionCoordinator;
             mCallback = callback;
         }
 
@@ -327,8 +332,12 @@
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
                 if (!(client instanceof FingerprintResetLockoutClient)) {
                     Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+                    // Given that onLockoutCleared() can happen at any time, and is not necessarily
+                    // coming from a specific client, set this to -1 to indicate it wasn't for a
+                    // specific request.
                     FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
-                            mLockoutCache, mLockoutResetDispatcher);
+                            mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+                            Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
                 } else {
                     Slog.d(mTag, "onLockoutCleared after resetLockout");
                     final FingerprintResetLockoutClient resetLockoutClient =
@@ -470,7 +479,8 @@
 
                         final HalSessionCallback resultController = new HalSessionCallback(mContext,
                                 mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
-                                lockoutResetDispatcher, () -> {
+                                lockoutResetDispatcher,
+                                biometricContext.getAuthSessionCoordinator(), () -> {
                             Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
                             mCurrentSession = null;
                         });
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index a0befea..36d56c8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -69,6 +71,7 @@
     private final SparseIntArray mFailedAttempts;
     private final AlarmManager mAlarmManager;
     private final LockoutReceiver mLockoutReceiver;
+    private final Handler mHandler;
 
     public LockoutFrameworkImpl(Context context, LockoutResetCallback lockoutResetCallback) {
         mContext = context;
@@ -77,6 +80,7 @@
         mFailedAttempts = new SparseIntArray();
         mAlarmManager = context.getSystemService(AlarmManager.class);
         mLockoutReceiver = new LockoutReceiver();
+        mHandler = new Handler(Looper.getMainLooper());
 
         context.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
                 RESET_FINGERPRINT_LOCKOUT, null /* handler */, Context.RECEIVER_EXPORTED);
@@ -127,9 +131,11 @@
     }
 
     private void scheduleLockoutResetForUser(int userId) {
-        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+        mHandler.post(() -> {
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                 SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
                 getLockoutResetIntentForUser(userId));
+        });
     }
 
     private PendingIntent getLockoutResetIntentForUser(int userId) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0f17139..1dc2725 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -152,6 +152,7 @@
 import com.android.internal.net.VpnProfile;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.LinkPropertiesUtils;
 import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.DeviceIdleInternal;
@@ -230,7 +231,35 @@
      * <p>If retries have exceeded the length of this array, the last entry in the array will be
      * used as a repeating interval.
      */
-    private static final long[] IKEV2_VPN_RETRY_DELAYS_SEC = {1L, 2L, 5L, 30L, 60L, 300L, 900L};
+    private static final long[] IKEV2_VPN_RETRY_DELAYS_MS =
+            {1_000L, 2_000L, 5_000L, 30_000L, 60_000L, 300_000L, 900_000L};
+
+    /**
+     * A constant to pass to {@link IkeV2VpnRunner#scheduleStartIkeSession(long)} to mean the
+     * delay should be computed automatically with backoff.
+     */
+    private static final long RETRY_DELAY_AUTO_BACKOFF = -1;
+
+    /**
+     * How long to wait before trying to migrate the IKE connection when NetworkCapabilities or
+     * LinkProperties change in a way that may require migration.
+     *
+     * This delay is useful to avoid multiple migration tries (e.g. when a network changes
+     * both its NC and LP at the same time, e.g. when it first connects) and to minimize the
+     * cases where an old list of addresses is detected for the network.
+     *
+     * In practice, the IKE library reads the LinkProperties of the passed network with
+     * the synchronous {@link ConnectivityManager#getLinkProperties(Network)}, which means in
+     * most cases the race would resolve correctly, but this delay increases the chance that
+     * it correctly is.
+     * Further, using the synchronous method in the IKE library is actually dangerous because
+     * it is racy (it races with {@code IkeNetworkCallbackBase#onLost} and it should be fixed
+     * by using callbacks instead. When that happens, the race within IKE is fixed but the
+     * race between that callback and the one in IkeV2VpnRunner becomes a much bigger problem,
+     * and this delay will be necessary to ensure the correct link address list is used.
+     */
+    private static final long IKE_DELAY_ON_NC_LP_CHANGE_MS = 300;
+
     /**
      * Largest profile size allowable for Platform VPNs.
      *
@@ -619,14 +648,14 @@
         /**
          * Retrieves the next retry delay
          *
-         * <p>If retries have exceeded the IKEV2_VPN_RETRY_DELAYS_SEC, the last entry in
+         * <p>If retries have exceeded the size of IKEV2_VPN_RETRY_DELAYS_MS, the last entry in
          * the array will be used as a repeating interval.
          */
-        public long getNextRetryDelaySeconds(int retryCount) {
-            if (retryCount >= IKEV2_VPN_RETRY_DELAYS_SEC.length) {
-                return IKEV2_VPN_RETRY_DELAYS_SEC[IKEV2_VPN_RETRY_DELAYS_SEC.length - 1];
+        public long getNextRetryDelayMs(int retryCount) {
+            if (retryCount >= IKEV2_VPN_RETRY_DELAYS_MS.length) {
+                return IKEV2_VPN_RETRY_DELAYS_MS[IKEV2_VPN_RETRY_DELAYS_MS.length - 1];
             } else {
-                return IKEV2_VPN_RETRY_DELAYS_SEC[retryCount];
+                return IKEV2_VPN_RETRY_DELAYS_MS[retryCount];
             }
         }
 
@@ -679,6 +708,14 @@
                 boolean isIpv4) {
             return MtuUtils.getMtu(childProposals, maxMtu, underlyingMtu, isIpv4);
         }
+
+        /** Verify the binder calling UID is the one passed in arguments */
+        public void verifyCallingUidAndPackage(Context context, String packageName, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            if (getAppUid(context, packageName, userId) != callingUid) {
+                throw new SecurityException(packageName + " does not belong to uid " + callingUid);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -726,7 +763,7 @@
         mUserManager = mContext.getSystemService(UserManager.class);
 
         mPackage = VpnConfig.LEGACY_VPN;
-        mOwnerUID = getAppUid(mPackage, mUserId);
+        mOwnerUID = getAppUid(mContext, mPackage, mUserId);
         mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
 
         try {
@@ -823,7 +860,7 @@
     }
 
     /**
-     * Chooses whether to force all connections to go though VPN.
+     * Chooses whether to force all connections to go through VPN.
      *
      * Used to enable/disable legacy VPN lockdown.
      *
@@ -831,7 +868,7 @@
      * {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling
      * that function will be replaced and saved with the always-on state.
      *
-     * @param lockdown whether to prevent all traffic outside of a VPN.
+     * @param lockdown whether to prevent all traffic outside of the VPN.
      */
     public synchronized void setLockdown(boolean lockdown) {
         enforceControlPermissionOrInternalCaller();
@@ -1108,6 +1145,7 @@
             mAlwaysOn = false;
         }
 
+        final boolean oldLockdownState = mLockdown;
         mLockdown = (mAlwaysOn && lockdown);
         mLockdownAllowlist = (mLockdown && lockdownAllowlist != null)
                 ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist))
@@ -1118,6 +1156,13 @@
         if (isCurrentPreparedPackage(packageName)) {
             updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
             setVpnForcedLocked(mLockdown);
+
+            // Lockdown forces the VPN to be non-bypassable (see #agentConnect) because it makes
+            // no sense for a VPN to be bypassable when connected but not when not connected.
+            // As such, changes in lockdown need to restart the agent.
+            if (mNetworkAgent != null && oldLockdownState != mLockdown) {
+                startNewNetworkAgent(mNetworkAgent, "Lockdown mode changed");
+            }
         } else {
             // Prepare this app. The notification will update as a side-effect of updateState().
             // It also calls setVpnForcedLocked().
@@ -1355,7 +1400,8 @@
         // We can't just check that packageName matches mPackage, because if the app was uninstalled
         // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
         // calling package may not be the same as the prepared package. Check both UID and package.
-        return getAppUid(packageName, mUserId) == mOwnerUID && mPackage.equals(packageName);
+        return getAppUid(mContext, packageName, mUserId) == mOwnerUID
+                && mPackage.equals(packageName);
     }
 
     /** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -1396,7 +1442,7 @@
 
             Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
             mPackage = newPackage;
-            mOwnerUID = getAppUid(newPackage, mUserId);
+            mOwnerUID = getAppUid(mContext, newPackage, mUserId);
             mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
             try {
                 mNms.allowProtect(mOwnerUID);
@@ -1417,7 +1463,7 @@
         // Check if the caller is authorized.
         enforceControlPermissionOrInternalCaller();
 
-        final int uid = getAppUid(packageName, mUserId);
+        final int uid = getAppUid(mContext, packageName, mUserId);
         if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
             // Authorization for nonexistent packages (or fake ones) can't be updated.
             return false;
@@ -1497,11 +1543,11 @@
                 || isVpnServicePreConsented(context, packageName);
     }
 
-    private int getAppUid(final String app, final int userId) {
+    private static int getAppUid(final Context context, final String app, final int userId) {
         if (VpnConfig.LEGACY_VPN.equals(app)) {
             return Process.myUid();
         }
-        PackageManager pm = mContext.getPackageManager();
+        PackageManager pm = context.getPackageManager();
         final long token = Binder.clearCallingIdentity();
         try {
             return pm.getPackageUidAsUser(app, userId);
@@ -1630,6 +1676,10 @@
      */
     private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
         // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
+        // Strictly speaking, bypassability is affected by lockdown and therefore it's possible
+        // it doesn't actually change even if mConfig.allowBypass changed. It might be theoretically
+        // possible to do handover in this case, but this is far from obvious to VPN authors and
+        // it's simpler if the rule is just "can't update in place if you change allow bypass".
         if (oldConfig.allowBypass != mConfig.allowBypass) {
             Log.i(TAG, "Handover not possible due to changes to allowBypass");
             return false;
@@ -1671,10 +1721,11 @@
         mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
         updateState(DetailedState.CONNECTING, "agentConnect");
 
+        final boolean bypassable = mConfig.allowBypass && !mLockdown;
         final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder()
                 .setLegacyType(ConnectivityManager.TYPE_VPN)
                 .setLegacyTypeName("VPN")
-                .setBypassableVpn(mConfig.allowBypass && !mLockdown)
+                .setBypassableVpn(bypassable)
                 .setVpnRequiresValidation(mConfig.requiresInternetValidation)
                 .setLocalRoutesExcludedForVpn(mConfig.excludeLocalRoutes)
                 .build();
@@ -1688,7 +1739,7 @@
         capsBuilder.setTransportInfo(new VpnTransportInfo(
                 getActiveVpnType(),
                 mConfig.session,
-                mConfig.allowBypass,
+                bypassable,
                 expensive));
 
         // Only apps targeting Q and above can explicitly declare themselves as metered.
@@ -1719,6 +1770,10 @@
             Binder.restoreCallingIdentity(token);
         }
         updateState(DetailedState.CONNECTED, "agentConnect");
+        if (isIkev2VpnRunner()) {
+            final IkeSessionWrapper session = ((IkeV2VpnRunner) mVpnRunner).mSession;
+            if (null != session) session.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
+        }
     }
 
     private static boolean areLongLivedTcpConnectionsExpensive(@NonNull VpnRunner runner) {
@@ -1913,7 +1968,7 @@
     private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) {
         SortedSet<Integer> uids = new TreeSet<>();
         for (String app : packageNames) {
-            int uid = getAppUid(app, userId);
+            int uid = getAppUid(mContext, app, userId);
             if (uid != -1) uids.add(uid);
             // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
             // ConnectivityServiceTest.
@@ -3232,7 +3287,6 @@
                             prepareStatusIntent();
                         }
                         agentConnect(this::onValidationStatus);
-                        mSession.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
                         return; // Link properties are already sent.
                     } else {
                         // Underlying networks also set in agentConnect()
@@ -3349,7 +3403,6 @@
                     if (!removedAddrs.isEmpty()) {
                         startNewNetworkAgent(
                                 mNetworkAgent, "MTU too low for IPv6; restarting network agent");
-                        mSession.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
 
                         for (LinkAddress removed : removedAddrs) {
                             mTunnelIface.removeAddress(
@@ -3628,7 +3681,7 @@
             final VpnTransportInfo info = new VpnTransportInfo(
                     getActiveVpnType(),
                     mConfig.session,
-                    mConfig.allowBypass,
+                    mConfig.allowBypass && !mLockdown,
                     areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
             final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo());
             if (ncUpdateRequired) {
@@ -3718,13 +3771,20 @@
             }
         }
 
-        private void scheduleRetryNewIkeSession() {
+        /**
+         * Schedule starting an IKE session.
+         * @param delayMs the delay after which to try starting the session. This should be
+         *                RETRY_DELAY_AUTO_BACKOFF for automatic retries with backoff.
+         */
+        private void scheduleStartIkeSession(final long delayMs) {
             if (mScheduledHandleRetryIkeSessionFuture != null) {
                 Log.d(TAG, "There is a pending retrying task, skip the new retrying task");
                 return;
             }
-            final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++);
-            Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds.");
+            final long retryDelayMs = RETRY_DELAY_AUTO_BACKOFF != delayMs
+                    ? delayMs
+                    : mDeps.getNextRetryDelayMs(mRetryCount++);
+            Log.d(TAG, "Retry new IKE session after " + retryDelayMs + " milliseconds.");
             // If the default network is lost during the retry delay, the mActiveNetwork will be
             // null, and the new IKE session won't be established until there is a new default
             // network bringing up.
@@ -3735,7 +3795,7 @@
                         // Reset mScheduledHandleRetryIkeSessionFuture since it's already run on
                         // executor thread.
                         mScheduledHandleRetryIkeSessionFuture = null;
-                    }, retryDelay, TimeUnit.SECONDS);
+                    }, retryDelayMs, TimeUnit.MILLISECONDS);
         }
 
         /** Called when the NetworkCapabilities of underlying network is changed */
@@ -3744,20 +3804,26 @@
                     + mUnderlyingNetworkCapabilities + " to " + nc);
             final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
             mUnderlyingNetworkCapabilities = nc;
-            if (oldNc == null) {
-                // A new default network is available.
-                startOrMigrateIkeSession(mActiveNetwork);
-            } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
-                // Renew carrierConfig values.
-                maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
+            if (oldNc == null || !nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
+                // A new default network is available, or the subscription has changed.
+                // Try to migrate the session, or failing that, start a new one.
+                scheduleStartIkeSession(IKE_DELAY_ON_NC_LP_CHANGE_MS);
             }
         }
 
         /** Called when the LinkProperties of underlying network is changed */
         public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) {
-            mEventChanges.log("[UnderlyingNW] Lp changed from "
-                    + mUnderlyingLinkProperties + " to " + lp);
+            final LinkProperties oldLp = mUnderlyingLinkProperties;
+            mEventChanges.log("[UnderlyingNW] Lp changed from " + oldLp + " to " + lp);
             mUnderlyingLinkProperties = lp;
+            if (oldLp == null || !LinkPropertiesUtils.isIdenticalAllLinkAddresses(oldLp, lp)) {
+                // If some of the link addresses changed, the IKE session may need to be migrated
+                // or restarted, for example if the available IP families have changed or if the
+                // source address used has gone away. See IkeConnectionController#onNetworkSetByUser
+                // and IkeConnectionController#selectAndSetRemoteAddress for where this ends up
+                // re-evaluating the session.
+                scheduleStartIkeSession(IKE_DELAY_ON_NC_LP_CHANGE_MS);
+            }
         }
 
         class VpnConnectivityDiagnosticsCallback
@@ -4035,7 +4101,7 @@
                 markFailedAndDisconnect(exception);
                 return;
             } else {
-                scheduleRetryNewIkeSession();
+                scheduleStartIkeSession(RETRY_DELAY_AUTO_BACKOFF);
             }
 
             // Close all obsolete state, but keep VPN alive incase a usable network comes up.
@@ -4472,10 +4538,7 @@
     }
 
     private void verifyCallingUidAndPackage(String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (getAppUid(packageName, mUserId) != callingUid) {
-            throw new SecurityException(packageName + " does not belong to uid " + callingUid);
-        }
+        mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index ab261ac..f4c84e7 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -37,8 +37,11 @@
 import android.util.SparseArray;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.Locale;
+
 /**
  * Manages the user-visible device state notifications.
  */
@@ -56,15 +59,14 @@
     private final NotificationManager mNotificationManager;
     private final PackageManager mPackageManager;
 
-    // Stores the notification title and content indexed with the device state identifier.
-    private final SparseArray<NotificationInfo> mNotificationInfos;
-
     // The callback when a device state is requested to be canceled.
     private final Runnable mCancelStateRunnable;
 
+    private final NotificationInfoProvider mNotificationInfoProvider;
+
     DeviceStateNotificationController(@NonNull Context context, @NonNull Handler handler,
             @NonNull Runnable cancelStateRunnable) {
-        this(context, handler, cancelStateRunnable, getNotificationInfos(context),
+        this(context, handler, cancelStateRunnable, new NotificationInfoProvider(context),
                 context.getPackageManager(), context.getSystemService(NotificationManager.class));
     }
 
@@ -72,13 +74,13 @@
     DeviceStateNotificationController(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull Runnable cancelStateRunnable,
-            @NonNull SparseArray<NotificationInfo> notificationInfos,
+            @NonNull NotificationInfoProvider notificationInfoProvider,
             @NonNull PackageManager packageManager,
             @NonNull NotificationManager notificationManager) {
         mContext = context;
         mHandler = handler;
         mCancelStateRunnable = cancelStateRunnable;
-        mNotificationInfos = notificationInfos;
+        mNotificationInfoProvider = notificationInfoProvider;
         mPackageManager = packageManager;
         mNotificationManager = notificationManager;
         mContext.registerReceiver(
@@ -97,7 +99,7 @@
      * @param requestingAppUid the uid of the requesting app used to retrieve the app name.
      */
     void showStateActiveNotificationIfNeeded(int state, int requestingAppUid) {
-        NotificationInfo info = mNotificationInfos.get(state);
+        NotificationInfo info = getNotificationInfos().get(state);
         if (info == null || !info.hasActiveNotification()) {
             return;
         }
@@ -127,7 +129,7 @@
      * @param state the identifier of the device state being canceled.
      */
     void showThermalCriticalNotificationIfNeeded(int state) {
-        NotificationInfo info = mNotificationInfos.get(state);
+        NotificationInfo info = getNotificationInfos().get(state);
         if (info == null || !info.hasThermalCriticalNotification()) {
             return;
         }
@@ -148,7 +150,7 @@
      * @param state the identifier of the device state being canceled.
      */
     void showPowerSaveNotificationIfNeeded(int state) {
-        NotificationInfo info = mNotificationInfos.get(state);
+        NotificationInfo info = getNotificationInfos().get(state);
         if (info == null || !info.hasPowerSaveModeNotification()) {
             return;
         }
@@ -170,7 +172,7 @@
      * @param state the device state identifier.
      */
     void cancelNotification(int state) {
-        if (!mNotificationInfos.contains(state)) {
+        if (getNotificationInfos().get(state) == null) {
             return;
         }
         mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
@@ -221,69 +223,121 @@
         mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
     }
 
-    /**
-     * Loads the resources for the notifications. The device state identifiers and strings are
-     * stored in arrays. All the string arrays must have the same length and same order as the
-     * identifier array.
-     */
-    private static SparseArray<NotificationInfo> getNotificationInfos(Context context) {
-        final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();
+    private SparseArray<NotificationInfo> getNotificationInfos() {
+        Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
+        return mNotificationInfoProvider.getNotificationInfos(locale);
+    }
 
-        final int[] stateIdentifiers =
-                context.getResources().getIntArray(
-                        R.array.device_state_notification_state_identifiers);
-        final String[] names =
-                context.getResources().getStringArray(R.array.device_state_notification_names);
-        final String[] activeNotificationTitles =
-                context.getResources().getStringArray(
-                        R.array.device_state_notification_active_titles);
-        final String[] activeNotificationContents =
-                context.getResources().getStringArray(
-                        R.array.device_state_notification_active_contents);
-        final String[] thermalCriticalNotificationTitles =
-                context.getResources().getStringArray(
-                        R.array.device_state_notification_thermal_titles);
-        final String[] thermalCriticalNotificationContents =
-                context.getResources().getStringArray(
-                        R.array.device_state_notification_thermal_contents);
-        final String[] powerSaveModeNotificationTitles =
-                context.getResources().getStringArray(
-                        R.array.device_state_notification_power_save_titles);
-        final String[] powerSaveModeNotificationContents =
-                context.getResources().getStringArray(
-                        R.array.device_state_notification_power_save_contents);
+    @VisibleForTesting
+    public static class NotificationInfoProvider {
+        @NonNull
+        private final Context mContext;
+        private final Object mLock = new Object();
 
+        @GuardedBy("mLock")
+        @Nullable
+        private SparseArray<NotificationInfo> mCachedNotificationInfos;
 
-        if (stateIdentifiers.length != names.length
-                || stateIdentifiers.length != activeNotificationTitles.length
-                || stateIdentifiers.length != activeNotificationContents.length
-                || stateIdentifiers.length != thermalCriticalNotificationTitles.length
-                || stateIdentifiers.length != thermalCriticalNotificationContents.length
-                || stateIdentifiers.length != powerSaveModeNotificationTitles.length
-                || stateIdentifiers.length != powerSaveModeNotificationContents.length
-        ) {
-            throw new IllegalStateException(
-                    "The length of state identifiers and notification texts must match!");
+        @GuardedBy("mLock")
+        @Nullable
+        @VisibleForTesting
+        Locale mCachedLocale;
+
+        NotificationInfoProvider(@NonNull Context context) {
+            mContext = context;
         }
 
-        for (int i = 0; i < stateIdentifiers.length; i++) {
-            int identifier = stateIdentifiers[i];
-            if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
-                continue;
+        /**
+         * Loads the resources for the notifications. The device state identifiers and strings are
+         * stored in arrays. All the string arrays must have the same length and same order as the
+         * identifier array.
+         */
+        @NonNull
+        public SparseArray<NotificationInfo> getNotificationInfos(@NonNull Locale locale) {
+            synchronized (mLock) {
+                if (!locale.equals(mCachedLocale)) {
+                    refreshNotificationInfos(locale);
+                }
+                return mCachedNotificationInfos;
+            }
+        }
+
+
+        @VisibleForTesting
+        Locale getCachedLocale() {
+            synchronized (mLock) {
+                return mCachedLocale;
+            }
+        }
+
+        @VisibleForTesting
+        public void refreshNotificationInfos(Locale locale) {
+            synchronized (mLock) {
+                mCachedLocale = locale;
+                mCachedNotificationInfos = loadNotificationInfos();
+            }
+        }
+
+        @VisibleForTesting
+        public SparseArray<NotificationInfo> loadNotificationInfos() {
+            final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();
+
+            final int[] stateIdentifiers =
+                    mContext.getResources().getIntArray(
+                            R.array.device_state_notification_state_identifiers);
+            final String[] names =
+                    mContext.getResources().getStringArray(R.array.device_state_notification_names);
+            final String[] activeNotificationTitles =
+                    mContext.getResources().getStringArray(
+                            R.array.device_state_notification_active_titles);
+            final String[] activeNotificationContents =
+                    mContext.getResources().getStringArray(
+                            R.array.device_state_notification_active_contents);
+            final String[] thermalCriticalNotificationTitles =
+                    mContext.getResources().getStringArray(
+                            R.array.device_state_notification_thermal_titles);
+            final String[] thermalCriticalNotificationContents =
+                    mContext.getResources().getStringArray(
+                            R.array.device_state_notification_thermal_contents);
+            final String[] powerSaveModeNotificationTitles =
+                    mContext.getResources().getStringArray(
+                            R.array.device_state_notification_power_save_titles);
+            final String[] powerSaveModeNotificationContents =
+                    mContext.getResources().getStringArray(
+                            R.array.device_state_notification_power_save_contents);
+
+            if (stateIdentifiers.length != names.length
+                    || stateIdentifiers.length != activeNotificationTitles.length
+                    || stateIdentifiers.length != activeNotificationContents.length
+                    || stateIdentifiers.length != thermalCriticalNotificationTitles.length
+                    || stateIdentifiers.length != thermalCriticalNotificationContents.length
+                    || stateIdentifiers.length != powerSaveModeNotificationTitles.length
+                    || stateIdentifiers.length != powerSaveModeNotificationContents.length
+            ) {
+                throw new IllegalStateException(
+                        "The length of state identifiers and notification texts must match!");
             }
 
-            notificationInfos.put(
-                    identifier,
-                    new NotificationInfo(
-                            names[i], activeNotificationTitles[i], activeNotificationContents[i],
-                            thermalCriticalNotificationTitles[i],
-                            thermalCriticalNotificationContents[i],
-                            powerSaveModeNotificationTitles[i],
-                            powerSaveModeNotificationContents[i])
-            );
-        }
+            for (int i = 0; i < stateIdentifiers.length; i++) {
+                int identifier = stateIdentifiers[i];
+                if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
+                    continue;
+                }
 
-        return notificationInfos;
+                notificationInfos.put(
+                        identifier,
+                        new NotificationInfo(
+                                names[i],
+                                activeNotificationTitles[i],
+                                activeNotificationContents[i],
+                                thermalCriticalNotificationTitles[i],
+                                thermalCriticalNotificationContents[i],
+                                powerSaveModeNotificationTitles[i],
+                                powerSaveModeNotificationContents[i])
+                );
+            }
+            return notificationInfos;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 5e3990a..1acc208 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -514,6 +514,7 @@
 
     private boolean mIsEnabled;
     private boolean mIsInTransition;
+    private boolean mIsDisplayInternal;
 
     // The id of the thermal brightness throttling policy that should be used.
     private String mThermalBrightnessThrottlingDataId;
@@ -553,6 +554,8 @@
         mDisplayStatsId = mUniqueDisplayId.hashCode();
         mIsEnabled = logicalDisplay.isEnabledLocked();
         mIsInTransition = logicalDisplay.isInTransitionLocked();
+        mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked()
+                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
         mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
@@ -892,6 +895,9 @@
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
         final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+        final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
         final String thermalBrightnessThrottlingDataId =
                 mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
         mHandler.postAtTime(() -> {
@@ -924,7 +930,7 @@
                 mIsEnabled = isEnabled;
                 mIsInTransition = isInTransition;
             }
-
+            mIsDisplayInternal = isDisplayInternal;
             if (changed) {
                 updatePowerState();
             }
@@ -1810,10 +1816,11 @@
             // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
             // done in HighBrightnessModeController.
             if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
-                    && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
-                    && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+                    && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+                    && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
                     == 0) {
-                // We want to scale HDR brightness level with the SDR level
+                // We want to scale HDR brightness level with the SDR level, we also need to restore
+                // SDR brightness immediately when entering dim or low power mode.
                 animateValue = mHbmController.getHdrBrightnessValue();
             }
 
@@ -3074,9 +3081,7 @@
                 event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
                 ? -1f : convertToNits(event.getThermalMax());
 
-        if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
-                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
-                    .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+        if (mIsDisplayInternal) {
             FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
                     convertToNits(event.getInitialBrightness()),
                     convertToNits(event.getBrightness()),
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 23e606c..b36aede 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -399,6 +399,7 @@
 
     private boolean mIsEnabled;
     private boolean mIsInTransition;
+    private boolean mIsDisplayInternal;
 
     // The id of the thermal brightness throttling policy that should be used.
     private String mThermalBrightnessThrottlingDataId;
@@ -431,6 +432,8 @@
                 .getDisplayDeviceConfig();
         mIsEnabled = logicalDisplay.isEnabledLocked();
         mIsInTransition = logicalDisplay.isInTransitionLocked();
+        mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked()
+                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
         mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
         mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
@@ -708,6 +711,9 @@
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
         final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+        final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
         final String thermalBrightnessThrottlingDataId =
                 mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
 
@@ -742,6 +748,7 @@
                 mIsInTransition = isInTransition;
             }
 
+            mIsDisplayInternal = isDisplayInternal;
             if (changed) {
                 updatePowerState();
             }
@@ -1449,10 +1456,11 @@
             // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
             // done in HighBrightnessModeController.
             if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
-                    && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
-                    && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+                    && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+                    && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
                     == 0) {
-                // We want to scale HDR brightness level with the SDR level
+                // We want to scale HDR brightness level with the SDR level, we also need to restore
+                // SDR brightness immediately when entering dim or low power mode.
                 animateValue = mHbmController.getHdrBrightnessValue();
             }
 
@@ -2217,6 +2225,7 @@
         pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
         pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
         pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
+        pw.println("  mIsDisplayInternal=" + mIsDisplayInternal);
         synchronized (mCachedBrightnessInfo) {
             pw.println("  mCachedBrightnessInfo.brightness="
                     + mCachedBrightnessInfo.brightness.value);
@@ -2434,9 +2443,7 @@
         float appliedThermalCapNits =
                 event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
                 ? -1f : mDisplayBrightnessController.convertToNits(event.getThermalMax());
-        if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
-                && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
-                .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+        if (mIsDisplayInternal) {
             FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
                     mDisplayBrightnessController.convertToNits(event.getInitialBrightness()),
                     mDisplayBrightnessController.convertToNits(event.getBrightness()),
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 6511f4f..1e13974 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -38,7 +38,9 @@
     public static final int WAKE_LOCK_STATE_CHANGED = 4;
     public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 5;
 
-    private static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
+    @VisibleForTesting
+    static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
+
     private static final boolean DEBUG = false;
 
     @IntDef(flag = true, prefix = "WAKE_LOCK_", value = {
@@ -132,7 +134,7 @@
      * A utility to release all the wakelock acquired by the system
      */
     public void releaseAll() {
-        for (int i = WAKE_LOCK_PROXIMITY_POSITIVE; i < WAKE_LOCK_MAX; i++) {
+        for (int i = WAKE_LOCK_PROXIMITY_POSITIVE; i <= WAKE_LOCK_MAX; i++) {
             releaseWakelockInternal(i);
         }
     }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 01cae42..ad640b1 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -239,7 +239,7 @@
         String[] certs = mContext.getResources().getStringArray(
                 R.array.config_fontManagerServiceCerts);
 
-        if (mDebugCertFilePath != null && (Build.IS_USERDEBUG || Build.IS_ENG)) {
+        if (mDebugCertFilePath != null && Build.IS_DEBUGGABLE) {
             String[] tmp = new String[certs.length + 1];
             System.arraycopy(certs, 0, tmp, 0, certs.length);
             tmp[certs.length] = mDebugCertFilePath;
@@ -251,8 +251,8 @@
     }
 
     /**
-     * Add debug certificate to the cert list. This must be called only on userdebug/eng
-     * build.
+     * Add debug certificate to the cert list. This must be called only on debuggable build.
+     *
      * @param debugCertPath a debug certificate file path
      */
     public void addDebugCertificate(@Nullable String debugCertPath) {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index c039a83..6d82841d 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -105,8 +105,8 @@
         w.println("    Update font families with the new definitions.");
         w.println();
         w.println("install-debug-cert [cert file path]");
-        w.println("    Install debug certificate file. This command can be used only on userdebug");
-        w.println("    or eng device with root user.");
+        w.println("    Install debug certificate file. This command can be used only on");
+        w.println("    debuggable device with root user.");
         w.println();
         w.println("clear");
         w.println("    Remove all installed font files and reset to the initial state.");
diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS
index 0466d8a..4fea05d 100644
--- a/services/core/java/com/android/server/infra/OWNERS
+++ b/services/core/java/com/android/server/infra/OWNERS
@@ -1,3 +1,3 @@
 # Bug component: 655446
 
-include /core/java/android/service/cloudsearch/OWNERS
+srazdan@google.com
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d0669e7..5f45f91 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -686,13 +686,7 @@
 
     @NonNull
     private InputChannel createSpyWindowGestureMonitor(IBinder monitorToken, String name,
-            int displayId, int pid, int uid) {
-        final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
-                displayId);
-        if (sc == null) {
-            throw new IllegalArgumentException(
-                    "Could not create gesture monitor surface on display: " + displayId);
-        }
+            SurfaceControl sc, int displayId, int pid, int uid) {
         final InputChannel channel = createInputChannel(name);
 
         try {
@@ -749,9 +743,18 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            final InputChannel inputChannel =
-                            createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid);
-            return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken()));
+            final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
+                    displayId);
+            if (sc == null) {
+                throw new IllegalArgumentException(
+                        "Could not create gesture monitor surface on display: " + displayId);
+            }
+
+            final InputChannel inputChannel = createSpyWindowGestureMonitor(
+                    monitorToken, name, sc, displayId, pid, uid);
+            return new InputMonitor(inputChannel,
+                new InputMonitorHost(inputChannel.getToken()),
+                new SurfaceControl(sc, "IMS.monitorGestureInput"));
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ee4a6fe..a7e704e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -116,6 +116,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1229,6 +1230,26 @@
     }
 
     /**
+     * {@link LockPatternUtils#refreshStoredPinLength(int)}
+     * @param userId user id of the user whose pin length we want to save
+     * @return true/false depending on whether PIN length has been saved or not
+     */
+    @Override
+    public boolean refreshStoredPinLength(int userId) {
+        checkPasswordHavePermission();
+        synchronized (mSpManager) {
+            PasswordMetrics passwordMetrics = getUserPasswordMetrics(userId);
+            if (passwordMetrics != null) {
+                final long protectorId = getCurrentLskfBasedProtectorId(userId);
+                return mSpManager.refreshPinLengthOnDisk(passwordMetrics, protectorId, userId);
+            } else {
+                Log.w(TAG, "PasswordMetrics is not available");
+                return false;
+            }
+        }
+    }
+
+    /**
      * This API is cached; whenever the result would change,
      * {@link com.android.internal.widget.LockPatternUtils#invalidateCredentialTypeCache}
      * must be called.
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index de3a7ef..731ecad 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -121,6 +121,11 @@
     }
 
     @VisibleForTesting
+    public boolean isAutoPinConfirmSettingEnabled(int userId) {
+        return getBoolean(LockPatternUtils.AUTO_PIN_CONFIRM, false, userId);
+    }
+
+    @VisibleForTesting
     public void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) {
         ContentValues cv = new ContentValues();
         cv.put(COLUMN_KEY, key);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index a4dab72..8b8c5f6 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.locksettings;
 
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE;
 
@@ -545,6 +546,11 @@
         return null;
     }
 
+    @VisibleForTesting
+    public boolean isAutoPinConfirmationFeatureAvailable() {
+        return LockPatternUtils.isAutoPinConfirmFeatureAvailable();
+    }
+
     private synchronized boolean isWeaverAvailable() {
         if (mWeaver != null) {
             return true;
@@ -901,8 +907,8 @@
             LockscreenCredential credential, SyntheticPassword sp, int userId) {
         long protectorId = generateProtectorId();
         int pinLength = PIN_LENGTH_UNAVAILABLE;
-        if (LockPatternUtils.isAutoPinConfirmFeatureAvailable()) {
-            pinLength = derivePinLength(credential, userId);
+        if (isAutoPinConfirmationFeatureAvailable()) {
+            pinLength = derivePinLength(credential.size(), credential.isPin(), userId);
         }
         // There's no need to store password data about an empty LSKF.
         PasswordData pwd = credential.isNone() ? null :
@@ -978,13 +984,13 @@
         return protectorId;
     }
 
-    private int derivePinLength(LockscreenCredential credential, int userId) {
-        if (!credential.isPin()
-                || !mStorage.getBoolean(LockPatternUtils.AUTO_PIN_CONFIRM, false, userId)
-                || credential.size() < LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH) {
+    private int derivePinLength(int sizeOfCredential, boolean isPinCredential, int userId) {
+        if (!isPinCredential
+                || !mStorage.isAutoPinConfirmSettingEnabled(userId)
+                || sizeOfCredential < LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH) {
             return PIN_LENGTH_UNAVAILABLE;
         }
-        return credential.size();
+        return sizeOfCredential;
     }
 
     public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
@@ -1347,19 +1353,39 @@
             savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId);
             syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe.
         }
-        if (LockPatternUtils.isAutoPinConfirmFeatureAvailable()
-                && result.syntheticPassword != null && pwd != null) {
-            int expectedPinLength = derivePinLength(credential, userId);
-            if (pwd.pinLength != expectedPinLength) {
-                pwd.pinLength = expectedPinLength;
-                saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
-                syncState(userId);
-            }
-        }
         return result;
     }
 
     /**
+     * {@link LockPatternUtils#refreshStoredPinLength(int)}
+     * @param passwordMetrics passwordMetrics object containing the cached pin length
+     * @param userId userId of the user whose pin length we want to store on disk
+     * @param protectorId current LSKF based protectorId
+     * @return true/false depending on whether PIN length has been saved on disk
+     */
+    public boolean refreshPinLengthOnDisk(PasswordMetrics passwordMetrics,
+            long protectorId, int userId) {
+        if (!isAutoPinConfirmationFeatureAvailable()) {
+            return false;
+        }
+
+        byte[] pwdDataBytes = loadState(PASSWORD_DATA_NAME, protectorId, userId);
+        if (pwdDataBytes == null) {
+            return false;
+        }
+
+        PasswordData pwd = PasswordData.fromBytes(pwdDataBytes);
+        int pinLength = derivePinLength(passwordMetrics.length,
+                passwordMetrics.credType == CREDENTIAL_TYPE_PIN, userId);
+        if (pwd.pinLength != pinLength) {
+            pwd.pinLength = pinLength;
+            saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId);
+            syncState(userId);
+        }
+        return true;
+    }
+
+    /**
      * Tries to unlock a token-based SP protector (weak or strong), given its ID and the claimed
      * token.  On success, returns the user's synthetic password, and also does a Gatekeeper
      * verification to refresh the SID and HardwareAuthToken maintained by the system.
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index b82e3a3..c076c05 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
@@ -26,6 +27,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -108,6 +110,28 @@
                 && mComponentName.getClassName().equals(className);
     }
 
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + getDebugString());
+        prefix += "  ";
+        if (mProviderInfo == null) {
+            pw.println(prefix + "<provider info not received, yet>");
+        } else if (mProviderInfo.getRoutes().isEmpty()) {
+            pw.println(prefix + "<provider info has no routes>");
+        } else {
+            for (MediaRoute2Info route : mProviderInfo.getRoutes()) {
+                pw.printf("%s%s | %s\n", prefix, route.getId(), route.getName());
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getDebugString();
+    }
+
+    /** Returns a human-readable string describing the instance, for debugging purposes. */
+    protected abstract String getDebugString();
+
     public interface Callback {
         void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
         void onSessionCreated(@NonNull MediaRoute2Provider provider,
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 90451b1..72b8436 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -44,7 +44,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -83,10 +82,6 @@
         mHandler = new Handler(Looper.myLooper());
     }
 
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + getDebugString());
-    }
-
     public void setManagerScanning(boolean managerScanning) {
         if (mIsManagerScanning != managerScanning) {
             mIsManagerScanning = managerScanning;
@@ -488,11 +483,7 @@
     }
 
     @Override
-    public String toString() {
-        return getDebugString();
-    }
-
-    private String getDebugString() {
+    protected String getDebugString() {
         return TextUtils.formatSimple(
                 "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b)",
                 mComponentName.getPackageName(),
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 3c97aaf8..2d3b97b 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1751,6 +1751,7 @@
             String indent = prefix + "  ";
             pw.println(indent + "mRunning=" + mRunning);
 
+            mSystemProvider.dump(pw, prefix);
             mWatcher.dump(pw, prefix);
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 5ea2ca4..464a256 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,14 +16,22 @@
 
 package com.android.server.media;
 
+import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -42,6 +50,7 @@
 import android.media.session.PlaybackState;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
@@ -52,6 +61,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -73,6 +83,17 @@
  */
 // TODO(jaewan): Do not call service method directly -- introduce listener instead.
 public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
+
+    /**
+     * {@link MediaSession#setMediaButtonBroadcastReceiver(ComponentName)} throws an {@link
+     * IllegalArgumentException} if the provided {@link ComponentName} does not resolve to a valid
+     * {@link android.content.BroadcastReceiver broadcast receiver} for apps targeting Android U and
+     * above. For apps targeting Android T and below, the request will be ignored.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    static final long THROW_FOR_INVALID_BROADCAST_RECEIVER = 270049379L;
+
     private static final String TAG = "MediaSessionRecord";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -871,6 +892,22 @@
         }
     };
 
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    private static boolean componentNameExists(
+            @NonNull ComponentName componentName, @NonNull Context context, int userId) {
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mediaButtonIntent.setComponent(componentName);
+
+        UserHandle userHandle = UserHandle.of(userId);
+        PackageManager pm = context.getPackageManager();
+
+        List<ResolveInfo> resolveInfos =
+                pm.queryBroadcastReceiversAsUser(
+                        mediaButtonIntent, PackageManager.ResolveInfoFlags.of(0), userHandle);
+        return !resolveInfos.isEmpty();
+    }
+
     private final class SessionStub extends ISession.Stub {
         @Override
         public void destroySession() throws RemoteException {
@@ -955,7 +992,9 @@
         }
 
         @Override
+        @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
         public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
+            final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 //mPackageName has been verified in MediaSessionService.enforcePackageName().
@@ -970,6 +1009,20 @@
                         != 0) {
                     return;
                 }
+
+                if (!componentNameExists(receiver, mContext, mUserId)) {
+                    if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) {
+                        throw new IllegalArgumentException("Invalid component name: " + receiver);
+                    } else {
+                        Log.w(
+                                TAG,
+                                "setMediaButtonBroadcastReceiver(): "
+                                        + "Ignoring invalid component name="
+                                        + receiver);
+                    }
+                    return;
+                }
+
                 mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver);
                 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
             } finally {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5d5c621..6d2d2e4 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -392,6 +392,15 @@
         mCallback.onSessionUpdated(this, sessionInfo);
     }
 
+    @Override
+    protected String getDebugString() {
+        return TextUtils.formatSimple(
+                "SystemMR2Provider - package: %s, selected route id: %s, bluetooth impl: %s",
+                mComponentName.getPackageName(),
+                mSelectedRouteId,
+                mBluetoothRouteController.getClass().getSimpleName());
+    }
+
     private static class SessionCreationRequest {
         final long mRequestId;
         final String mRouteId;
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index c1171fa..2704f56 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -38,6 +38,7 @@
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PowerExemptionManager;
@@ -337,7 +338,7 @@
                 broadcastAllowlist, null /* filterExtrasForReceiver */, null);
         // Send to PermissionController for all new users, even if it may not be running for some
         // users
-        if (isPrivacySafetyLabelChangeNotificationsEnabled()) {
+        if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                     packageName, extras, 0,
                     mContext.getPackageManager().getPermissionControllerPackageName(),
@@ -389,9 +390,13 @@
     }
 
     /** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
-    public static boolean isPrivacySafetyLabelChangeNotificationsEnabled() {
+    public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
+        PackageManager packageManager = context.getPackageManager();
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false);
+                SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false)
+            && !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+            && !packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+            && !packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 6f7ce80..1aa1fd1 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4298,6 +4298,11 @@
         if (Process.isSdkSandboxUid(uid)) {
             uid = getBaseSdkSandboxUid();
         }
+        if (Process.isIsolatedUid(uid)
+                && mPermissionManager.getHotwordDetectionServiceProvider() != null
+                && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) {
+            uid = getIsolatedOwner(uid);
+        }
         final int callingUserId = UserHandle.getUserId(callingUid);
         final int appId = UserHandle.getAppId(uid);
         final Object obj = mSettings.getSettingBase(appId);
@@ -4334,6 +4339,11 @@
             if (Process.isSdkSandboxUid(uid)) {
                 uid = getBaseSdkSandboxUid();
             }
+            if (Process.isIsolatedUid(uid)
+                    && mPermissionManager.getHotwordDetectionServiceProvider() != null
+                    && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) {
+                uid = getIsolatedOwner(uid);
+            }
             final int appId = UserHandle.getAppId(uid);
             final Object obj = mSettings.getSettingBase(appId);
             if (obj instanceof SharedUserSetting) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
index 9683469..dc5915d 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -37,7 +37,7 @@
             Direction.TO_PROFILE
     })
     @Retention(RetentionPolicy.SOURCE)
-    @interface Direction {
+    public @interface Direction {
         int TO_PARENT = 0;
         int TO_PROFILE = 1;
     }
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 9eb73aa..48ee64f 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -25,6 +25,7 @@
 import android.provider.AlarmClock;
 import android.provider.MediaStore;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -88,6 +89,23 @@
                     .addDataType("vnd.android.cursor.item/calls")
                     .build();
 
+    /** Dial intent with mime type exclusively handled by managed profile. */
+    private static final DefaultCrossProfileIntentFilter DIAL_MIME_MANAGED_PROFILE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
     /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
     private static final DefaultCrossProfileIntentFilter DIAL_DATA =
             new DefaultCrossProfileIntentFilter.Builder(
@@ -103,6 +121,21 @@
                     .addDataScheme("voicemail")
                     .build();
 
+    /** Dial intent with data scheme exclusively handled by managed profile. */
+    private static final DefaultCrossProfileIntentFilter DIAL_DATA_MANAGED_PROFILE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
     /**
      * Dial intent with no data scheme or type can be handled by either managed profile or its
      * parent user.
@@ -117,6 +150,19 @@
                     .addCategory(Intent.CATEGORY_BROWSABLE)
                     .build();
 
+    /**
+     * Dial intent with no data scheme or type exclusively handled by managed profile.
+     */
+    private static final DefaultCrossProfileIntentFilter DIAL_RAW_MANAGED_PROFILE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .build();
+
     /** Pressing the call button can be handled by either managed profile or its parent user. */
     private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
             new DefaultCrossProfileIntentFilter.Builder(
@@ -143,6 +189,22 @@
                     .addDataScheme("mmsto")
                     .build();
 
+    /** SMS and MMS intent exclusively handled by the managed profile. */
+    private static final DefaultCrossProfileIntentFilter SMS_MMS_MANAGED_PROFILE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addAction(Intent.ACTION_SENDTO)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("sms")
+                    .addDataScheme("smsto")
+                    .addDataScheme("mms")
+                    .addDataScheme("mmsto")
+                    .build();
+
     /** Mobile network settings is always shown in the primary user. */
     private static final DefaultCrossProfileIntentFilter
             MOBILE_NETWORK_SETTINGS =
@@ -297,14 +359,12 @@
                     .build();
 
     public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
-        return Arrays.asList(
+        List<DefaultCrossProfileIntentFilter> filters =
+                new ArrayList<DefaultCrossProfileIntentFilter>();
+        filters.addAll(Arrays.asList(
                 EMERGENCY_CALL_MIME,
                 EMERGENCY_CALL_DATA,
-                DIAL_MIME,
-                DIAL_DATA,
-                DIAL_RAW,
                 CALL_BUTTON,
-                SMS_MMS,
                 SET_ALARM,
                 MEDIA_CAPTURE,
                 RECOGNIZE_SPEECH,
@@ -317,11 +377,13 @@
                 USB_DEVICE_ATTACHED,
                 ACTION_SEND,
                 HOME,
-                MOBILE_NETWORK_SETTINGS);
+                MOBILE_NETWORK_SETTINGS));
+        filters.addAll(getDefaultCrossProfileTelephonyIntentFilters(false));
+        return filters;
     }
 
-    /** Call intent with tel scheme */
-    private static final DefaultCrossProfileIntentFilter CALL =
+    /** Call intent with tel scheme exclusively handled my managed profile. */
+    private static final DefaultCrossProfileIntentFilter CALL_MANAGED_PROFILE =
             new DefaultCrossProfileIntentFilter.Builder(
                     DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
                     SKIP_CURRENT_PROFILE,
@@ -334,13 +396,22 @@
     /**
      * Returns default telephony related intent filters for managed profile.
      */
-    public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileTelephonyFilters() {
-        return Arrays.asList(
-                DIAL_DATA,
-                DIAL_MIME,
-                DIAL_RAW,
-                CALL,
-                SMS_MMS);
+    public static List<DefaultCrossProfileIntentFilter>
+            getDefaultCrossProfileTelephonyIntentFilters(boolean telephonyOnlyInManagedProfile) {
+        if (telephonyOnlyInManagedProfile) {
+            return Arrays.asList(
+                    DIAL_DATA_MANAGED_PROFILE,
+                    DIAL_MIME_MANAGED_PROFILE,
+                    DIAL_RAW_MANAGED_PROFILE,
+                    CALL_MANAGED_PROFILE,
+                    SMS_MMS_MANAGED_PROFILE);
+        } else {
+            return Arrays.asList(
+                    DIAL_DATA,
+                    DIAL_MIME,
+                    DIAL_RAW,
+                    SMS_MMS);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index ad8a9ba..1e0c95c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -49,6 +49,7 @@
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
 import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
@@ -497,6 +498,13 @@
             mPm.setUpCustomResolverActivity(pkg, pkgSetting);
         }
 
+        File appMetadataFile = new File(pkgSetting.getPath(), APP_METADATA_FILE_NAME);
+        if (appMetadataFile.exists()) {
+            pkgSetting.setAppMetadataFilePath(appMetadataFile.getAbsolutePath());
+        } else {
+            pkgSetting.setAppMetadataFilePath(null);
+        }
+
         if (pkg.getPackageName().equals("android")) {
             mPm.setPlatformPackage(pkg, pkgSetting);
         }
@@ -1135,22 +1143,22 @@
         // behavior.
         if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                 "MinInstallableTargetSdk__install_block_enabled",
-                true)) {
+                false)) {
             int minInstallableTargetSdk =
                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                             "MinInstallableTargetSdk__min_installable_target_sdk",
-                            PackageManagerService.MIN_INSTALLABLE_TARGET_SDK);
+                            0);
 
             // Determine if enforcement is in strict mode
             boolean strictMode = false;
 
             if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                     "MinInstallableTargetSdk__install_block_strict_mode_enabled",
-                    true)) {
+                    false)) {
                 if (parsedPackage.getTargetSdkVersion()
                         < DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                         "MinInstallableTargetSdk__strict_mode_target_sdk",
-                        PackageManagerService.MIN_INSTALLABLE_TARGET_SDK)) {
+                        0)) {
                     strictMode = true;
                 }
             }
@@ -2946,7 +2954,7 @@
                 }
                 // Send to PermissionController for all update users, even if it may not be running
                 // for some users
-                if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled()) {
+                if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
                     mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
                             mPm.mRequiredPermissionControllerPackage, null /*finishedReceiver*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e1a1ac..b5108af 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -560,14 +560,6 @@
     // How many required verifiers can be on the system.
     private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;
 
-    /**
-     * Specifies the minimum target SDK version an apk must specify in order to be installed
-     * on the system. This improves security and privacy by blocking low
-     * target sdk apps as malware can target older sdk versions to avoid
-     * the enforcement of new API behavior.
-     */
-    public static final int MIN_INSTALLABLE_TARGET_SDK = Build.VERSION_CODES.M;
-
     // Compilation reasons.
     // TODO(b/260124949): Clean this up with the legacy dexopt code.
     public static final int REASON_FIRST_BOOT = 0;
@@ -2364,6 +2356,32 @@
                         SystemClock.uptimeMillis() - startTime);
             }
 
+            // If this is first boot or first boot after OTA then set the file path to the app
+            // metadata files for preloaded packages.
+            if (mFirstBoot || isDeviceUpgrading()) {
+                ArrayMap<String, String> paths = systemConfig.getAppMetadataFilePaths();
+                for (Map.Entry<String, String> entry : paths.entrySet()) {
+                    String pkgName = entry.getKey();
+                    String path = entry.getValue();
+                    File file = new File(path);
+                    if (!file.exists()) {
+                        path = null;
+                    }
+                    PackageSetting disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(pkgName);
+                    if (disabledPkgSetting == null) {
+                        PackageSetting pkgSetting = mSettings.getPackageLPr(pkgName);
+                        if (pkgSetting != null) {
+                            pkgSetting.setAppMetadataFilePath(path);
+                        } else {
+                            Slog.w(TAG, "Cannot set app metadata file for nonexistent package "
+                                    + pkgName);
+                        }
+                    } else {
+                        disabledPkgSetting.setAppMetadataFilePath(path);
+                    }
+                }
+            }
+
             // Rebuild the live computer since some attributes have been rebuilt.
             mLiveComputer = createLiveComputer();
 
@@ -5164,12 +5182,16 @@
                 throw new ParcelableException(
                         new PackageManager.NameNotFoundException(packageName));
             }
-            try {
-                File file = new File(ps.getPath(), APP_METADATA_FILE_NAME);
-                return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
-            } catch (FileNotFoundException e) {
-                return null;
+            String filePath = ps.getAppMetadataFilePath();
+            if (filePath != null) {
+                File file = new File(filePath);
+                try {
+                    return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+                } catch (FileNotFoundException e) {
+                    return null;
+                }
             }
+            return null;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 89f46fe..eea6720 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -124,10 +124,12 @@
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.net.URISyntaxException;
@@ -348,6 +350,8 @@
                     return runDisableVerificationForUid();
                 case "set-silent-updates-policy":
                     return runSetSilentUpdatesPolicy();
+                case "get-app-metadata":
+                    return runGetAppMetadata();
                 default: {
                     if (ART_SERVICE_COMMANDS.contains(cmd)) {
                         if (DexOptHelper.useArtService()) {
@@ -3541,6 +3545,30 @@
         return 1;
     }
 
+    private int runGetAppMetadata() {
+        final PrintWriter pw = getOutPrintWriter();
+        String pkgName = getNextArgRequired();
+        ParcelFileDescriptor pfd = null;
+        try {
+            pfd = mInterface.getAppMetadataFd(pkgName, mContext.getUserId());
+        } catch (RemoteException e) {
+            pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]");
+            return -1;
+        }
+        if (pfd != null) {
+            try (BufferedReader br = new BufferedReader(
+                    new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd)))) {
+                while (br.ready()) {
+                    pw.println(br.readLine());
+                }
+            } catch (IOException e) {
+                pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]");
+                return -1;
+            }
+        }
+        return 1;
+    }
+
     private int runArtServiceCommand() {
         try (var in = ParcelFileDescriptor.dup(getInFileDescriptor());
                 var out = ParcelFileDescriptor.dup(getOutFileDescriptor());
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 839ff41..7fda092 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -189,6 +189,9 @@
     @NonNull
     private UUID mDomainSetId;
 
+    @Nullable
+    private String mAppMetadataFilePath;
+
     /**
      * Snapshot support.
      */
@@ -655,6 +658,7 @@
         updateAvailable = other.updateAvailable;
         forceQueryableOverride = other.forceQueryableOverride;
         mDomainSetId = other.mDomainSetId;
+        mAppMetadataFilePath = other.mAppMetadataFilePath;
 
         usesSdkLibraries = other.usesSdkLibraries != null
                 ? Arrays.copyOf(other.usesSdkLibraries,
@@ -1171,6 +1175,15 @@
         return this;
     }
 
+    /**
+     * @param path to app metadata file
+     */
+    public PackageSetting setAppMetadataFilePath(String path) {
+        mAppMetadataFilePath = path;
+        onChanged();
+        return this;
+    }
+
     @NonNull
     @Override
     public long getVersionCode() {
@@ -1579,11 +1592,16 @@
         return mDomainSetId;
     }
 
+    @DataClass.Generated.Member
+    public @Nullable String getAppMetadataFilePath() {
+        return mAppMetadataFilePath;
+    }
+
     @DataClass.Generated(
-            time = 1678228625853L,
+            time = 1680917079522L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "private  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate  boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate  boolean updateAvailable\nprivate  boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate  boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate  boolean updateAvailable\nprivate  boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 417ba07..e8f89d3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2905,6 +2905,11 @@
         serializer.attributeLongHex(null, "loadingCompletedTime",
                 pkg.getLoadingCompletedTime());
 
+        if (pkg.getAppMetadataFilePath() != null) {
+            serializer.attribute(null, "appMetadataFilePath",
+                    pkg.getAppMetadataFilePath());
+        }
+
         writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
                 pkg.getUsesSdkLibrariesVersionsMajor());
 
@@ -2994,6 +2999,10 @@
 
         serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
 
+        if (pkg.getAppMetadataFilePath() != null) {
+            serializer.attribute(null, "appMetadataFilePath", pkg.getAppMetadataFilePath());
+        }
+
         writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
                 pkg.getUsesSdkLibrariesVersionsMajor());
 
@@ -3762,6 +3771,7 @@
         float loadingProgress = 0;
         long loadingCompletedTime = 0;
         UUID domainSetId;
+        String appMetadataFilePath = null;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
@@ -3799,6 +3809,7 @@
             volumeUuid = parser.getAttributeValue(null, "volumeUuid");
             categoryHint = parser.getAttributeInt(null, "categoryHint",
                     ApplicationInfo.CATEGORY_UNDEFINED);
+            appMetadataFilePath = parser.getAttributeValue(null, "appMetadataFilePath");
 
             String domainSetIdString = parser.getAttributeValue(null, "domainSetId");
 
@@ -3942,7 +3953,8 @@
                     .setUpdateAvailable(updateAvailable)
                     .setForceQueryableOverride(installedForceQueryable)
                     .setLoadingProgress(loadingProgress)
-                    .setLoadingCompletedTime(loadingCompletedTime);
+                    .setLoadingCompletedTime(loadingCompletedTime)
+                    .setAppMetadataFilePath(appMetadataFilePath);
             // Handle legacy string here for single-user mode
             final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
             if (enabledStr != null) {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 3a0ff27..f839648 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -105,4 +105,10 @@
      */
     @Nullable
     String getSecondaryCpuAbiLegacy();
+
+    /**
+     * @return the app metadata file path.
+     */
+    @Nullable
+    String getAppMetadataFilePath();
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index e437be8..2007079 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -17,10 +17,12 @@
 package com.android.server.rollback;
 
 import android.annotation.AnyThread;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
@@ -68,6 +70,9 @@
 final class RollbackPackageHealthObserver implements PackageHealthObserver {
     private static final String TAG = "RollbackPackageHealthObserver";
     private static final String NAME = "rollback-observer";
+    private static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot";
+    private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
+            | ApplicationInfo.FLAG_SYSTEM;
 
     private final Context mContext;
     private final Handler mHandler;
@@ -114,10 +119,10 @@
             // For native crashes, we will directly roll back any available rollbacks
             // Note: For non-native crashes the rollback-all step has higher impact
             impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
-        } else if (mitigationCount == 1 && getAvailableRollback(failedPackage) != null) {
+        } else if (getAvailableRollback(failedPackage) != null) {
             // Rollback is available, we may get a callback into #execute
             impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
-        } else if (mitigationCount > 1 && anyRollbackAvailable) {
+        } else if (anyRollbackAvailable) {
             // If any rollbacks are available, we will commit them
             impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
         }
@@ -133,14 +138,10 @@
             return true;
         }
 
-        if (mitigationCount == 1) {
-            RollbackInfo rollback = getAvailableRollback(failedPackage);
-            if (rollback == null) {
-                Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
-                return false;
-            }
+        RollbackInfo rollback = getAvailableRollback(failedPackage);
+        if (rollback != null) {
             mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
-        } else if (mitigationCount > 1) {
+        } else {
             mHandler.post(() -> rollbackAll(rollbackReason));
         }
 
@@ -153,6 +154,30 @@
         return NAME;
     }
 
+    @Override
+    public boolean isPersistent() {
+        return true;
+    }
+
+    @Override
+    public boolean mayObservePackage(String packageName) {
+        if (mContext.getSystemService(RollbackManager.class)
+                .getAvailableRollbacks().isEmpty()) {
+            return false;
+        }
+        return isPersistentSystemApp(packageName);
+    }
+
+    private boolean isPersistentSystemApp(@NonNull String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     private void assertInWorkerThread() {
         Preconditions.checkState(mHandler.getLooper().isCurrentThread());
     }
@@ -425,6 +450,7 @@
                 markStagedSessionHandled(rollback.getRollbackId());
                 // Wait for all pending staged sessions to get handled before rebooting.
                 if (isPendingStagedSessionsEmpty()) {
+                    SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true");
                     mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
                 }
             }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 433d807..0ce17de 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -21,6 +21,7 @@
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
+import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED;
 import static android.hardware.graphics.common.Hdr.DOLBY_VISION;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -750,6 +751,8 @@
                         return pullPendingIntentsPerPackage(atomTag, data);
                     case FrameworkStatsLog.HDR_CAPABILITIES:
                         return pullHdrCapabilities(atomTag, data);
+                    case FrameworkStatsLog.CACHED_APPS_HIGH_WATERMARK:
+                        return pullCachedAppsHighWatermark(atomTag, data);
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -950,6 +953,7 @@
         registerPendingIntentsPerPackagePuller();
         registerPinnerServiceStats();
         registerHdrCapabilitiesPuller();
+        registerCachedAppsHighWatermarkPuller();
     }
 
     private void initAndRegisterNetworkStatsPullers() {
@@ -4721,13 +4725,22 @@
         boolean userDisabledHdrConversion = hdrConversionMode == HDR_CONVERSION_PASSTHROUGH;
         int forceHdrFormat = preferredHdrType == HDR_TYPE_INVALID ? 0 : preferredHdrType;
         boolean hasDolbyVisionIssue = hasDolbyVisionIssue(display);
+        byte[] hdrOutputTypes = toBytes(displayManager.getSupportedHdrOutputTypes());
+        boolean hdrOutputControlSupported = hdrConversionMode != HDR_CONVERSION_UNSUPPORTED;
 
-        pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
-                new byte[0], userDisabledHdrConversion, forceHdrFormat, hasDolbyVisionIssue));
+        pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, hdrOutputTypes,
+                userDisabledHdrConversion, forceHdrFormat, hasDolbyVisionIssue,
+                hdrOutputControlSupported));
 
         return StatsManager.PULL_SUCCESS;
     }
 
+    private int pullCachedAppsHighWatermark(int atomTag, List<StatsEvent> pulledData) {
+        pulledData.add(LocalServices.getService(ActivityManagerInternal.class)
+                .getCachedAppsHighWatermarkStats(atomTag, true));
+        return StatsManager.PULL_SUCCESS;
+    }
+
     private boolean hasDolbyVisionIssue(Display display) {
         AtomicInteger modesSupportingDolbyVision = new AtomicInteger();
         Arrays.stream(display.getSupportedModes())
@@ -4773,6 +4786,16 @@
         );
     }
 
+    private void registerCachedAppsHighWatermarkPuller() {
+        final int tagId = FrameworkStatsLog.CACHED_APPS_HIGH_WATERMARK;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
     int pullSystemServerPinnerStats(int atomTag, List<StatsEvent> pulledData) {
         PinnerService pinnerService = LocalServices.getService(PinnerService.class);
         List<PinnedFileStats> pinnedFileStats = pinnerService.dumpDataForStatsd();
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 1ab7f362..9cf0834 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -124,6 +124,7 @@
     private static final Set<Integer> SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST = new HashSet<>(
             Arrays.asList(
                     USAGE_TOUCH,
+                    USAGE_ACCESSIBILITY,
                     USAGE_PHYSICAL_EMULATION,
                     USAGE_HARDWARE_FEEDBACK));
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f5cb613..2160ce1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5315,6 +5315,13 @@
             if (finishing || isState(STOPPED)) {
                 displayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
             }
+            // Because starting window was transferred, this activity may be a trampoline which has
+            // been occluded by next activity. If it has added windows, set client visibility
+            // immediately to avoid the client getting RELAYOUT_RES_FIRST_TIME from relayout and
+            // drawing an unnecessary frame.
+            if (startingMoved && !firstWindowDrawn && hasChild()) {
+                setClientVisible(false);
+            }
         } else {
             if (!appTransition.isTransitionSet()
                     && appTransition.isReady()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1944b3f..90af4c6 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -23,7 +23,6 @@
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
 import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
-import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.Intent.EXTRA_INTENT;
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -503,8 +502,7 @@
             @ActivityInterceptorCallback.OrderedId int orderId,
             @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
         if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
-            return info.getIntent() != null && info.getIntent().getAction() != null
-                    && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY);
+            return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
         }
         return true;
     }
@@ -513,8 +511,7 @@
             @ActivityInterceptorCallback.OrderedId int orderId,
             @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
         if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
-            return info.getIntent() != null && info.getIntent().getAction() != null
-                    && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY);
+            return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 064af0f..abf66bc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -38,7 +38,6 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -1244,9 +1243,7 @@
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
 
-        boolean isSandboxedActivity = (intent != null && intent.getAction() != null
-                && intent.getAction().equals(ACTION_START_SANDBOXED_ACTIVITY));
-        if (isSandboxedActivity) {
+        if (intent != null && intent.isSandboxActivity(mContext)) {
             SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
                     SdkSandboxManagerLocal.class);
             sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
@@ -1503,7 +1500,7 @@
                         .setCallingPid(callingPid)
                         .setCallingPackage(intent.getPackage())
                         .setActivityInfo(a)
-                        .setActivityOptions(options.toBundle())
+                        .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
                         // To start the dream from background, we need to start it from a persistent
                         // system process. Here we set the real calling uid to the system server uid
                         .setRealCallingUid(Binder.getCallingUid())
@@ -1651,7 +1648,7 @@
                     .setResultWho(resultWho)
                     .setRequestCode(requestCode)
                     .setStartFlags(startFlags)
-                    .setActivityOptions(bOptions)
+                    .setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
                     .setUserId(userId)
                     .setIgnoreTargetSecurity(ignoreTargetSecurity)
                     .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
@@ -1701,7 +1698,7 @@
                 .setVoiceInteractor(interactor)
                 .setStartFlags(startFlags)
                 .setProfilerInfo(profilerInfo)
-                .setActivityOptions(bOptions)
+                .setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
                 .setUserId(userId)
                 .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
                 .execute();
@@ -1728,7 +1725,7 @@
                     .setCallingPackage(callingPackage)
                     .setCallingFeatureId(callingFeatureId)
                     .setResolvedType(resolvedType)
-                    .setActivityOptions(bOptions)
+                    .setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
                     .setUserId(userId)
                     .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
                     .execute();
@@ -5356,7 +5353,7 @@
             if (app != null && app.getPid() > 0) {
                 ArrayList<Integer> firstPids = new ArrayList<Integer>();
                 firstPids.add(app.getPid());
-                dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null, null);
+                dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null, null, null);
             }
 
             File lastTracesFile = null;
@@ -5507,6 +5504,31 @@
         return checkPermission(permission, -1, sourceUid) == PackageManager.PERMISSION_GRANTED;
     }
 
+    /**
+     * Wrap the {@link ActivityOptions} in {@link SafeActivityOptions} and attach caller options
+     * that allow using the callers permissions to start background activities.
+     */
+    private SafeActivityOptions createSafeActivityOptionsWithBalAllowed(
+            @Nullable ActivityOptions options) {
+        if (options == null) {
+            options = ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+        } else if (options.getPendingIntentBackgroundActivityStartMode()
+                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+            options.setPendingIntentBackgroundActivityStartMode(
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+        }
+        return new SafeActivityOptions(options);
+    }
+
+    /**
+     * Wrap the options {@link Bundle} in {@link SafeActivityOptions} and attach caller options
+     * that allow using the callers permissions to start background activities.
+     */
+    private SafeActivityOptions createSafeActivityOptionsWithBalAllowed(@Nullable Bundle bOptions) {
+        return createSafeActivityOptionsWithBalAllowed(ActivityOptions.fromBundle(bOptions));
+    }
+
     final class H extends Handler {
         static final int REPORT_TIME_TRACKER_MSG = 1;
         static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index a83a033..3dc3be9 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -135,12 +135,6 @@
                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d",
                         organizer.asBinder(), uid);
                 if (mOrganizersByFeatureIds.get(feature) != null) {
-                    if (mOrganizersByFeatureIds.get(feature).mOrganizer.asBinder()
-                            .isBinderAlive()) {
-                        throw new IllegalStateException(
-                                "Replacing existing organizer currently unsupported");
-                    }
-
                     mOrganizersByFeatureIds.remove(feature).destroy();
                     Slog.d(TAG, "Replacing dead organizer for feature=" + feature);
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 6ed2025..339b6ec 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -128,7 +128,7 @@
 import com.android.internal.statusbar.LetterboxDetails;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.ScreenshotRequest;
-import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
 import com.android.internal.view.AppearanceRegion;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.LocalServices;
@@ -1081,11 +1081,11 @@
                 // The index of the provider and corresponding insets types cannot change at
                 // runtime as ensured in WMS. Make use of the index in the provider directly
                 // to access the latest provided size at runtime.
-                final TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider =
+                final TriFunction<DisplayFrames, WindowContainer, Rect, Integer> frameProvider =
                         getFrameProvider(win, i, INSETS_OVERRIDE_INDEX_INVALID);
                 final InsetsFrameProvider.InsetsSizeOverride[] overrides =
                         provider.getInsetsSizeOverrides();
-                final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
+                final SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>>
                         overrideProviders;
                 if (overrides != null) {
                     overrideProviders = new SparseArray<>();
@@ -1106,7 +1106,7 @@
         }
     }
 
-    private static TriConsumer<DisplayFrames, WindowContainer, Rect> getFrameProvider(
+    private static TriFunction<DisplayFrames, WindowContainer, Rect, Integer> getFrameProvider(
             WindowState win, int index, int overrideIndex) {
         return (displayFrames, windowContainer, inOutFrame) -> {
             final LayoutParams lp = win.mAttrs.forRotation(displayFrames.mRotation);
@@ -1152,6 +1152,7 @@
                     inOutFrame.set(sTmpRect2);
                 }
             }
+            return ifp.getFlags();
         };
     }
 
@@ -1179,7 +1180,7 @@
         }
     }
 
-    TriConsumer<DisplayFrames, WindowContainer, Rect> getImeSourceFrameProvider() {
+    TriFunction<DisplayFrames, WindowContainer, Rect, Integer> getImeSourceFrameProvider() {
         return (displayFrames, windowContainer, inOutFrame) -> {
             WindowState windowState = windowContainer.asWindowState();
             if (windowState == null) {
@@ -1198,6 +1199,7 @@
             } else {
                 inOutFrame.inset(windowState.mGivenContentInsets);
             }
+            return 0;
         };
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 3e03b99..b7eaf25 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -47,7 +47,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
@@ -73,8 +73,9 @@
     private @Nullable InsetsControlTarget mFakeControlTarget;
 
     private @Nullable ControlAdapter mAdapter;
-    private TriConsumer<DisplayFrames, WindowContainer, Rect> mFrameProvider;
-    private SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> mOverrideFrameProviders;
+    private TriFunction<DisplayFrames, WindowContainer, Rect, Integer> mFrameProvider;
+    private SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>>
+            mOverrideFrameProviders;
     private final SparseArray<Rect> mOverrideFrames = new SparseArray<Rect>();
     private boolean mIsLeashReadyForDispatching;
     private final Rect mSourceFrame = new Rect();
@@ -149,8 +150,8 @@
      *                               resulting frame that should be reported to given window type.
      */
     void setWindowContainer(@Nullable WindowContainer windowContainer,
-            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
-            @Nullable SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
+            @Nullable TriFunction<DisplayFrames, WindowContainer, Rect, Integer> frameProvider,
+            @Nullable SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>>
                     overrideFrameProviders) {
         if (mWindowContainer != null) {
             if (mControllable) {
@@ -203,7 +204,7 @@
             if (mServerVisible) {
                 mTmpRect.set(mWindowContainer.getBounds());
                 if (mFrameProvider != null) {
-                    mFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                    mFrameProvider.apply(mWindowContainer.getDisplayContent().mDisplayFrames,
                             mWindowContainer, mTmpRect);
                 }
             } else {
@@ -216,8 +217,11 @@
 
         mSourceFrame.set(frame);
         if (mFrameProvider != null) {
-            mFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
-                    mWindowContainer, mSourceFrame);
+            final int flags = mFrameProvider.apply(
+                    mWindowContainer.getDisplayContent().mDisplayFrames,
+                    mWindowContainer,
+                    mSourceFrame);
+            mSource.setFlags(flags);
         }
         updateSourceFrameForServerVisibility();
 
@@ -233,10 +237,10 @@
                 } else {
                     overrideFrame = new Rect(frame);
                 }
-                final TriConsumer<DisplayFrames, WindowContainer, Rect> provider =
+                final TriFunction<DisplayFrames, WindowContainer, Rect, Integer> provider =
                         mOverrideFrameProviders.get(windowType);
                 if (provider != null) {
-                    mOverrideFrameProviders.get(windowType).accept(
+                    mOverrideFrameProviders.get(windowType).apply(
                             mWindowContainer.getDisplayContent().mDisplayFrames, mWindowContainer,
                             overrideFrame);
                 }
@@ -274,7 +278,7 @@
         source.setVisible(mSource.isVisible());
         mTmpRect.set(frame);
         if (mFrameProvider != null) {
-            mFrameProvider.accept(displayFrames, mWindowContainer, mTmpRect);
+            mFrameProvider.apply(displayFrames, mWindowContainer, mTmpRect);
         }
         source.setFrame(mTmpRect);
         return source;
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 2e5ab1a..7572a64 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -221,11 +221,11 @@
                 if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                     mChoreographer.postFrameCallback(this::startAnimations);
                 }
-
-                // Some animations (e.g. move animations) require the initial transform to be
-                // applied immediately.
-                applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
             }
+
+            // Some animations (e.g. move animations) require the initial transform to be
+            // applied immediately.
+            applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
index c099628..8c79875 100644
--- a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
+++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
@@ -26,8 +26,8 @@
 final class TaskFpsCallbackController {
 
     private final Context mContext;
-    private final HashMap<ITaskFpsCallback, Long> mTaskFpsCallbacks;
-    private final HashMap<ITaskFpsCallback, IBinder.DeathRecipient> mDeathRecipients;
+    private final HashMap<IBinder, Long> mTaskFpsCallbacks;
+    private final HashMap<IBinder, IBinder.DeathRecipient> mDeathRecipients;
 
     TaskFpsCallbackController(Context context) {
         mContext = context;
@@ -36,32 +36,42 @@
     }
 
     void registerListener(int taskId, ITaskFpsCallback callback) {
-        if (mTaskFpsCallbacks.containsKey(callback)) {
+        if (callback == null) {
+            return;
+        }
+
+        IBinder binder = callback.asBinder();
+        if (mTaskFpsCallbacks.containsKey(binder)) {
             return;
         }
 
         final long nativeListener = nativeRegister(callback, taskId);
-        mTaskFpsCallbacks.put(callback, nativeListener);
+        mTaskFpsCallbacks.put(binder, nativeListener);
 
         final IBinder.DeathRecipient deathRecipient = () -> unregisterListener(callback);
         try {
-            callback.asBinder().linkToDeath(deathRecipient, 0);
-            mDeathRecipients.put(callback, deathRecipient);
+            binder.linkToDeath(deathRecipient, 0);
+            mDeathRecipients.put(binder, deathRecipient);
         } catch (RemoteException e) {
             // ignore
         }
     }
 
     void unregisterListener(ITaskFpsCallback callback) {
-        if (!mTaskFpsCallbacks.containsKey(callback)) {
+        if (callback == null) {
             return;
         }
 
-        callback.asBinder().unlinkToDeath(mDeathRecipients.get(callback), 0);
-        mDeathRecipients.remove(callback);
+        IBinder binder = callback.asBinder();
+        if (!mTaskFpsCallbacks.containsKey(binder)) {
+            return;
+        }
 
-        nativeUnregister(mTaskFpsCallbacks.get(callback));
-        mTaskFpsCallbacks.remove(callback);
+        binder.unlinkToDeath(mDeathRecipients.get(binder), 0);
+        mDeathRecipients.remove(binder);
+
+        nativeUnregister(mTaskFpsCallbacks.get(binder));
+        mTaskFpsCallbacks.remove(binder);
     }
 
     private static native long nativeRegister(ITaskFpsCallback callback, int taskId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8fecf11..99d0ea8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8526,6 +8526,14 @@
             //                    while in overview
             return;
         }
+        final WindowState w = t.getWindowState();
+        if (w != null) {
+            final Task task = w.getTask();
+            if (task != null && w.mTransitionController.isTransientHide(task)) {
+                // Don't disturb transient animation by accident touch.
+                return;
+            }
+        }
 
         ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s",
                 t);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 482e172..678d4c8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5336,6 +5336,17 @@
                     && imeTarget.compareTo(this) <= 0;
             return inTokenWithAndAboveImeTarget;
         }
+
+        // The condition is for the system dialog not belonging to any Activity.
+        // (^FLAG_NOT_FOCUSABLE & FLAG_ALT_FOCUSABLE_IM) means the dialog is still focusable but
+        // should be placed above the IME window.
+        if ((mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM))
+                == FLAG_ALT_FOCUSABLE_IM && isTrustedOverlay() && canAddInternalSystemWindow()) {
+            // Check the current IME target so that it does not lift this window above the IME if
+            // the Z-order of the current IME layering target is greater than it.
+            final WindowState imeTarget = getImeLayeringTarget();
+            return imeTarget != null && imeTarget != this && imeTarget.compareTo(this) <= 0;
+        }
         return false;
     }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b01d08c..439ad76 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -485,17 +485,7 @@
     }
     dump += "\n";
 
-    mInputManager->getReader().dump(dump);
-    dump += "\n";
-
-    mInputManager->getBlocker().dump(dump);
-    dump += "\n";
-
-    mInputManager->getProcessor().dump(dump);
-    dump += "\n";
-
-    mInputManager->getDispatcher().dump(dump);
-    dump += "\n";
+    mInputManager->dump(dump);
 }
 
 bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -760,7 +750,6 @@
 
 void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
     ATRACE_CALL();
-    mInputManager->getBlocker().notifyInputDevicesChanged(inputDevices);
     JNIEnv* env = jniEnv();
 
     size_t count = inputDevices.size();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 702602a..415440b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -632,6 +632,38 @@
     }
 
     /**
+     * Returns all the {@code policyKeys} set by any admin that share the same
+     * {@link PolicyKey#getIdentifier()} as the provided {@code policyDefinition}.
+     *
+     * <p>For example, getLocalPolicyKeysSetByAllAdmins(PERMISSION_GRANT) returns all permission
+     * grants set by any admin.
+     *
+     * <p>Note that this will always return at most one item for policies that do not require
+     * additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
+     * {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
+     *
+     */
+    @NonNull
+    <V> Set<PolicyKey> getLocalPolicyKeysSetByAllAdmins(
+            @NonNull PolicyDefinition<V> policyDefinition,
+            int userId) {
+        Objects.requireNonNull(policyDefinition);
+
+        synchronized (mLock) {
+            if (policyDefinition.isGlobalOnlyPolicy() || !mLocalPolicies.contains(userId)) {
+                return Set.of();
+            }
+            Set<PolicyKey> keys = new HashSet<>();
+            for (PolicyKey key : mLocalPolicies.get(userId).keySet()) {
+                if (key.hasSameIdentifierAs(policyDefinition.getPolicyKey())) {
+                    keys.add(key);
+                }
+            }
+            return keys;
+        }
+    }
+
+    /**
      * Returns all user restriction policies set by the given admin.
      *
      * <p>Pass in {@link UserHandle#USER_ALL} for {@code userId} to get global restrictions set by
@@ -985,8 +1017,12 @@
             int userId = user.id;
             // Apply local policies present on parent to newly created child profile.
             UserInfo parentInfo = mUserManager.getProfileParent(userId);
-            if (parentInfo == null || parentInfo.getUserHandle().getIdentifier() == userId) return;
-
+            if (parentInfo == null || parentInfo.getUserHandle().getIdentifier() == userId) {
+                return;
+            }
+            if (!mLocalPolicies.contains(parentInfo.getUserHandle().getIdentifier())) {
+                return;
+            }
             for (Map.Entry<PolicyKey, PolicyState<?>> entry : mLocalPolicies.get(
                     parentInfo.getUserHandle().getIdentifier()).entrySet()) {
                 enforcePolicyOnUser(userId, entry.getValue());
@@ -1210,6 +1246,31 @@
         synchronized (mLock) {
             clear();
             new DevicePoliciesReaderWriter().readFromFileLocked();
+            reapplyAllPolicies();
+        }
+    }
+
+    private <V> void reapplyAllPolicies() {
+        for (PolicyKey policy : mGlobalPolicies.keySet()) {
+            PolicyState<?> policyState = mGlobalPolicies.get(policy);
+            // Policy definition and value will always be of the same type
+            PolicyDefinition<V> policyDefinition =
+                    (PolicyDefinition<V>) policyState.getPolicyDefinition();
+            PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+            enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL);
+        }
+        for (int i = 0; i < mLocalPolicies.size(); i++) {
+            int userId = mLocalPolicies.keyAt(i);
+            for (PolicyKey policy : mLocalPolicies.get(userId).keySet()) {
+                PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
+                // Policy definition and value will always be of the same type
+                PolicyDefinition<V> policyDefinition =
+                        (PolicyDefinition<V>) policyState.getPolicyDefinition();
+                PolicyValue<V> policyValue =
+                        (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+                enforcePolicy(policyDefinition, policyValue, userId);
+
+            }
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5cad4e2..18fcafa 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -85,6 +85,7 @@
 import static android.Manifest.permission.SET_TIME;
 import static android.Manifest.permission.SET_TIME_ZONE;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+import static android.accounts.AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
@@ -280,6 +281,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.app.admin.AccountTypePolicyKey;
 import android.app.admin.BooleanPolicyValue;
 import android.app.admin.BundlePolicyValue;
 import android.app.admin.ComponentNamePolicyValue;
@@ -531,6 +533,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -1139,6 +1142,11 @@
                     }
                 }
             }
+
+            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                calculateHasIncompatibleAccounts();
+            }
+
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)
                     && userHandle == mOwners.getDeviceOwnerUserId()) {
                 mBugreportCollectionManager.checkForPendingBugreportAfterBoot();
@@ -1252,6 +1260,8 @@
             } else if (ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)) {
                 notifyIfManagedSubscriptionsAreUnavailable(
                         UserHandle.of(userHandle), /* managedProfileAvailable= */ true);
+            } else if (LOGIN_ACCOUNTS_CHANGED_ACTION.equals(action)) {
+                calculateHasIncompatibleAccounts();
             }
         }
 
@@ -2104,6 +2114,7 @@
         filter.addAction(Intent.ACTION_USER_STOPPED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        filter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
         filter.addAction(ACTION_MANAGED_PROFILE_UNAVAILABLE);
         filter.addAction(ACTION_MANAGED_PROFILE_AVAILABLE);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -2130,7 +2141,7 @@
         mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
 
         mDeviceManagementResourcesProvider.load();
-        if (isPermissionCheckFlagEnabled()) {
+        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
             mDevicePolicyEngine.load();
         }
 
@@ -3279,8 +3290,10 @@
 
         policy.validatePasswordOwner();
         updateMaximumTimeToLockLocked(userHandle);
-        updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle);
-        updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+        if (!isPolicyEngineForFinanceFlagEnabled()) {
+            updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle);
+            updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+        }
         if (policy.mStatusBarDisabled) {
             setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
         }
@@ -3592,7 +3605,7 @@
         }
 
         startOwnerService(userId, "start-user");
-        if (isPermissionCheckFlagEnabled()) {
+        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
             mDevicePolicyEngine.handleStartUser(userId);
         }
     }
@@ -3619,7 +3632,7 @@
 
     void handleUnlockUser(int userId) {
         startOwnerService(userId, "unlock-user");
-        if (isPermissionCheckFlagEnabled()) {
+        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
             mDevicePolicyEngine.handleUnlockUser(userId);
         }
     }
@@ -3631,7 +3644,7 @@
     void handleStopUser(int userId) {
         updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
         mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
-        if (isPermissionCheckFlagEnabled()) {
+        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
             mDevicePolicyEngine.handleStopUser(userId);
         }
     }
@@ -7711,30 +7724,72 @@
     private void updateTelephonyCrossProfileIntentFilters(int parentUserId, int profileUserId,
             boolean enableWorkTelephony) {
         try {
-            String packageName = mContext.getOpPackageName();
-            if (enableWorkTelephony) {
-                // Reset call/sms cross profile intent filters to be handled by managed profile.
-                for (DefaultCrossProfileIntentFilter filter :
-                        DefaultCrossProfileIntentFiltersUtils
-                                .getDefaultManagedProfileTelephonyFilters()) {
-                    IntentFilter intentFilter = filter.filter.getIntentFilter();
-                    if (!mIPackageManager.removeCrossProfileIntentFilter(intentFilter, packageName,
-                            profileUserId, parentUserId, filter.flags)) {
-                        Slogf.w(LOG_TAG,
-                                "Failed to remove cross-profile intent filter: " + intentFilter);
-                    }
-
-                    mIPackageManager.addCrossProfileIntentFilter(intentFilter, packageName,
-                            parentUserId, profileUserId, PackageManager.SKIP_CURRENT_PROFILE);
+            // This should only occur when managed profile is being removed.
+            if (!enableWorkTelephony && profileUserId == UserHandle.USER_NULL) {
+                mIPackageManager.clearCrossProfileIntentFilters(parentUserId,
+                        mContext.getPackageName());
+                return;
+            }
+            for (DefaultCrossProfileIntentFilter filter :
+                    DefaultCrossProfileIntentFiltersUtils
+                            .getDefaultCrossProfileTelephonyIntentFilters(!enableWorkTelephony)) {
+                if (removeCrossProfileIntentFilter(filter, parentUserId, profileUserId)) {
+                    Slogf.w(LOG_TAG,
+                            "Failed to remove cross-profile intent filter: "
+                                    + filter.filter.getIntentFilter() + ", enableWorkTelephony: "
+                                    + enableWorkTelephony);
                 }
-            } else {
-                mIPackageManager.clearCrossProfileIntentFilters(parentUserId, packageName);
+            }
+            for (DefaultCrossProfileIntentFilter filter :
+                    DefaultCrossProfileIntentFiltersUtils
+                            .getDefaultCrossProfileTelephonyIntentFilters(enableWorkTelephony)) {
+                addCrossProfileIntentFilter(filter, parentUserId, profileUserId);
             }
         } catch (RemoteException re) {
             Slogf.wtf(LOG_TAG, "Error updating telephony cross profile intent filters", re);
         }
     }
 
+    void addCrossProfileIntentFilter(DefaultCrossProfileIntentFilter filter, int parentUserId,
+            int profileUserId)
+            throws RemoteException {
+        if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PROFILE) {
+            mIPackageManager.addCrossProfileIntentFilter(
+                    filter.filter.getIntentFilter(),
+                    mContext.getOpPackageName(),
+                    parentUserId,
+                    profileUserId,
+                    filter.flags);
+        } else {
+            mIPackageManager.addCrossProfileIntentFilter(
+                    filter.filter.getIntentFilter(),
+                    mContext.getOpPackageName(),
+                    profileUserId,
+                    parentUserId,
+                    filter.flags);
+        }
+    }
+
+    boolean removeCrossProfileIntentFilter(DefaultCrossProfileIntentFilter filter, int parentUserId,
+            int profileUserId)
+            throws RemoteException {
+        if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PROFILE) {
+            return mIPackageManager.removeCrossProfileIntentFilter(
+                    filter.filter.getIntentFilter(),
+                    mContext.getOpPackageName(),
+                    parentUserId,
+                    profileUserId,
+                    filter.flags);
+        } else {
+            return mIPackageManager.removeCrossProfileIntentFilter(
+                    filter.filter.getIntentFilter(),
+                    mContext.getOpPackageName(),
+                    profileUserId,
+                    parentUserId,
+                    filter.flags);
+        }
+    }
+
     /**
      * @param factoryReset null: legacy behaviour, false: attempt to remove user, true: attempt to
      *                     factory reset
@@ -9515,6 +9570,7 @@
 
         synchronized (getLockObject()) {
             enforceCanSetDeviceOwnerLocked(caller, admin, userId, hasIncompatibleAccountsOrNonAdb);
+
             Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId),
                     "Invalid component " + admin + " for device owner");
             final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
@@ -10204,7 +10260,9 @@
         policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
         policy.mAffiliationIds.clear();
         policy.mLockTaskPackages.clear();
-        updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId);
+        if (!isPolicyEngineForFinanceFlagEnabled()) {
+            updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId);
+        }
         policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
         saveSettingsLocked(userId);
 
@@ -10994,7 +11052,7 @@
             return false;
         }
 
-        if (!isPermissionCheckFlagEnabled()) {
+        if (!isPermissionCheckFlagEnabled() && !isPolicyEngineForFinanceFlagEnabled()) {
             // TODO: Figure out if something like this needs to be restored for policy engine
             final ComponentName profileOwner = getProfileOwnerAsUser(userId);
             if (profileOwner == null) {
@@ -11009,17 +11067,6 @@
 
         return true;
     }
-
-    private void enforceCanCallLockTaskLocked(CallerIdentity caller) {
-        Preconditions.checkCallAuthorization(isProfileOwner(caller)
-                || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
-
-        final int userId =  caller.getUserId();
-        if (!canUserUseLockTaskLocked(userId)) {
-            throw new SecurityException("User " + userId + " is not allowed to use lock task");
-        }
-    }
-
     private void enforceCanQueryLockTaskLocked(ComponentName who, String callerPackageName) {
         CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         final int userId = caller.getUserId();
@@ -11047,6 +11094,16 @@
         return enforcingAdmin;
     }
 
+    private void enforceCanCallLockTaskLocked(CallerIdentity caller) {
+        Preconditions.checkCallAuthorization(isProfileOwner(caller)
+                || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
+
+        final int userId =  caller.getUserId();
+        if (!canUserUseLockTaskLocked(userId)) {
+            throw new SecurityException("User " + userId + " is not allowed to use lock task");
+        }
+    }
+
     private boolean isSystemUid(CallerIdentity caller) {
         return UserHandle.isSameApp(caller.getUid(), Process.SYSTEM_UID);
     }
@@ -13978,16 +14035,28 @@
             caller = getCallerIdentity(who);
         }
         synchronized (getLockObject()) {
-            final ActiveAdmin ap;
             if (isPermissionCheckFlagEnabled()) {
+                int affectedUser = getAffectedUser(parent);
                 EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                         who,
                         MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
                         caller.getPackageName(),
-                        getAffectedUser(parent)
+                        affectedUser
                 );
-                ap = enforcingAdmin.getActiveAdmin();
+                if (disabled) {
+                    mDevicePolicyEngine.setLocalPolicy(
+                            PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+                            enforcingAdmin,
+                            new BooleanPolicyValue(disabled),
+                            affectedUser);
+                } else {
+                    mDevicePolicyEngine.removeLocalPolicy(
+                            PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+                            enforcingAdmin,
+                            affectedUser);
+                }
             } else {
+                final ActiveAdmin ap;
                 Objects.requireNonNull(who, "ComponentName is null");
                 /*
                  * When called on the parent DPM instance (parent == true), affects active admin
@@ -14004,13 +14073,13 @@
                     ap = getParentOfAdminIfRequired(
                             getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
                 }
+                if (disabled) {
+                    ap.accountTypesWithManagementDisabled.add(accountType);
+                } else {
+                    ap.accountTypesWithManagementDisabled.remove(accountType);
+                }
+                saveSettingsLocked(UserHandle.getCallingUserId());
             }
-            if (disabled) {
-                ap.accountTypesWithManagementDisabled.add(accountType);
-            } else {
-                ap.accountTypesWithManagementDisabled.remove(accountType);
-            }
-            saveSettingsLocked(UserHandle.getCallingUserId());
         }
     }
 
@@ -14028,41 +14097,64 @@
         }
         CallerIdentity caller;
         Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+        final ArraySet<String> resultSet = new ArraySet<>();
         if (isPermissionCheckFlagEnabled()) {
+            int affectedUser = parent ? getProfileParentId(userId) : userId;
             caller = getCallerIdentity(callerPackageName);
             if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-                    caller.getPackageName(), userId)
+                    callerPackageName, affectedUser)
                     && !hasFullCrossUsersPermission(caller, userId)) {
                 throw new SecurityException("Caller does not have permission to call this on user: "
-                        + userId);
+                        + affectedUser);
             }
-        } else {
-            caller = getCallerIdentity();
-            Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
-        }
+            Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins(
+                    PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED,
+                    affectedUser);
 
-        synchronized (getLockObject()) {
-            final ArraySet<String> resultSet = new ArraySet<>();
+            for (PolicyKey key : keys) {
+                if (!(key instanceof AccountTypePolicyKey)) {
+                    throw new IllegalStateException("PolicyKey for "
+                            + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type "
+                            + "AccountTypePolicyKey");
+                }
+                AccountTypePolicyKey parsedKey =
+                        (AccountTypePolicyKey) key;
+                String accountType = Objects.requireNonNull(parsedKey.getAccountType());
 
-            if (!parent) {
-                final DevicePolicyData policy = getUserData(userId);
-                for (ActiveAdmin admin : policy.mAdminList) {
-                    resultSet.addAll(admin.accountTypesWithManagementDisabled);
+                Boolean disabled = mDevicePolicyEngine.getResolvedPolicy(
+                        PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+                        affectedUser);
+                if (disabled != null && disabled) {
+                    resultSet.add(accountType);
                 }
             }
 
-            // Check if there's a profile owner of an org-owned device and the method is called for
-            // the parent user of this profile owner.
-            final ActiveAdmin orgOwnedAdmin =
-                    getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
-            final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
-                    || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
-            if (shouldGetParentAccounts) {
-                resultSet.addAll(
-                        orgOwnedAdmin.getParentActiveAdmin().accountTypesWithManagementDisabled);
+        } else {
+            caller = getCallerIdentity();
+            Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
+
+            synchronized (getLockObject()) {
+                if (!parent) {
+                    final DevicePolicyData policy = getUserData(userId);
+                    for (ActiveAdmin admin : policy.mAdminList) {
+                        resultSet.addAll(admin.accountTypesWithManagementDisabled);
+                    }
+                }
+
+                // Check if there's a profile owner of an org-owned device and the method is called
+                // for the parent user of this profile owner.
+                final ActiveAdmin orgOwnedAdmin =
+                        getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
+                final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
+                        || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
+                if (shouldGetParentAccounts) {
+                    resultSet.addAll(
+                            orgOwnedAdmin.getParentActiveAdmin()
+                                    .accountTypesWithManagementDisabled);
+                }
             }
-            return resultSet.toArray(new String[resultSet.size()]);
         }
+        return resultSet.toArray(new String[resultSet.size()]);
     }
 
     @Override
@@ -14637,7 +14729,7 @@
         if (isPolicyEngineForFinanceFlagEnabled()) {
             EnforcingAdmin enforcingAdmin;
             synchronized (getLockObject()) {
-                enforcingAdmin = enforceCanCallLockTaskLocked(who, callerPackageName);
+                enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
             }
             if (packages.length == 0) {
                 mDevicePolicyEngine.removeLocalPolicy(
@@ -14764,8 +14856,7 @@
         if (isPolicyEngineForFinanceFlagEnabled()) {
             EnforcingAdmin enforcingAdmin;
             synchronized (getLockObject()) {
-                enforcingAdmin = enforceCanCallLockTaskLocked(who,
-                        callerPackageName);
+                enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
                 enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
             }
             LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
@@ -14842,6 +14933,7 @@
                 }
 
                 final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages;
+                // TODO(b/278438525): handle in the policy engine
                 if (!lockTaskPackages.isEmpty()) {
                     Slogf.d(LOG_TAG,
                             "User id " + userId + " not affiliated. Clearing lock task packages");
@@ -18305,6 +18397,26 @@
         return isUserAffiliatedWithDeviceLocked(userId);
     }
 
+    private boolean hasIncompatibleAccountsOnAnyUser() {
+        if (mHasIncompatibleAccounts == null) {
+            // Hasn't loaded for the first time yet - assume the worst
+            return true;
+        }
+
+        for (boolean hasIncompatible : mHasIncompatibleAccounts.values()) {
+            if (hasIncompatible) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean hasIncompatibleAccounts(int userId) {
+        return mHasIncompatibleAccounts == null ? true
+                : mHasIncompatibleAccounts.getOrDefault(userId, /* default= */ false);
+    }
+
     /**
      * Return true if a given user has any accounts that'll prevent installing a device or profile
      * owner {@code owner}.
@@ -18342,7 +18454,7 @@
                 }
             }
 
-            boolean compatible = !hasIncompatibleAccounts(am, accounts);
+            boolean compatible = !hasIncompatibleAccounts(userId);
             if (compatible) {
                 Slogf.w(LOG_TAG, "All accounts are compatible");
             } else {
@@ -18352,37 +18464,67 @@
         });
     }
 
-    private boolean hasIncompatibleAccounts(AccountManager am, Account[] accounts) {
-        // TODO(b/244284408): Add test
-        final String[] feature_allow =
-                { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
-        final String[] feature_disallow =
-                { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
-
-        for (Account account : accounts) {
-            if (hasAccountFeatures(am, account, feature_disallow)) {
-                Slogf.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
-                return true;
-            }
-            if (!hasAccountFeatures(am, account, feature_allow)) {
-                Slogf.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
-                return true;
-            }
-        }
-
-        return false;
+    @Override
+    public void calculateHasIncompatibleAccounts() {
+        new CalculateHasIncompatibleAccountsTask().executeOnExecutor(
+                AsyncTask.THREAD_POOL_EXECUTOR, null);
     }
 
-    private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) {
-        try {
-            // TODO(267156507): Restore without blocking binder thread
-            return false;
-//            return am.hasFeatures(account, features, null, null).getResult();
-        } catch (Exception e) {
-            Slogf.w(LOG_TAG, "Failed to get account feature", e);
+    @Nullable
+    private volatile Map<Integer, Boolean> mHasIncompatibleAccounts;
+
+    class CalculateHasIncompatibleAccountsTask extends AsyncTask<
+            Void, Void, Map<Integer, Boolean>> {
+        private static final String[] FEATURE_ALLOW =
+                {DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED};
+        private static final String[] FEATURE_DISALLOW =
+                {DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED};
+
+        @Override
+        protected Map<Integer, Boolean> doInBackground(Void... args) {
+            List<UserInfo> users = mUserManagerInternal.getUsers(/* excludeDying= */ true);
+            Map<Integer, Boolean> results = new HashMap<>();
+            for (UserInfo userInfo : users) {
+                results.put(userInfo.id, userHasIncompatibleAccounts(userInfo.id));
+            }
+
+            return results;
+        }
+
+        private boolean userHasIncompatibleAccounts(int id) {
+            AccountManager am = mContext.createContextAsUser(UserHandle.of(id), /* flags= */ 0)
+                    .getSystemService(AccountManager.class);
+            Account[] accounts = am.getAccounts();
+
+            for (Account account : accounts) {
+                if (hasAccountFeatures(am, account, FEATURE_DISALLOW)) {
+                    return true;
+                }
+                if (!hasAccountFeatures(am, account, FEATURE_ALLOW)) {
+                    return true;
+                }
+            }
+
             return false;
         }
-    }
+
+        @Override
+        protected void onPostExecute(Map<Integer, Boolean> results) {
+            mHasIncompatibleAccounts = Collections.unmodifiableMap(results);
+
+            Slogf.i(LOG_TAG, "Finished calculating hasIncompatibleAccountsTask");
+        }
+
+        private static boolean hasAccountFeatures(AccountManager am, Account account,
+                String[] features) {
+            try {
+                return am.hasFeatures(account, features, null, null).getResult();
+            } catch (Exception e) {
+                Slogf.w(LOG_TAG, "Failed to get account feature", e);
+                return false;
+            }
+        }
+    };
 
     private boolean isAdb(CallerIdentity caller) {
         return isShellUid(caller) || isRootUid(caller);
@@ -22214,26 +22356,6 @@
         }
     }
 
-    private boolean hasIncompatibleAccountsOnAnyUser() {
-        long callingIdentity = Binder.clearCallingIdentity();
-        try {
-            for (UserInfo user : mUserManagerInternal.getUsers(/* excludeDying= */ true)) {
-                AccountManager am = mContext.createContextAsUser(
-                        UserHandle.of(user.id), /* flags= */ 0)
-                        .getSystemService(AccountManager.class);
-                Account[] accounts = am.getAccounts();
-
-                if (hasIncompatibleAccounts(am, accounts)) {
-                    return true;
-                }
-            }
-
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-    }
-
     private void setBypassDevicePolicyManagementRoleQualificationStateInternal(
             String currentRoleHolder, boolean allowBypass) {
         boolean stateChanged = false;
@@ -22474,11 +22596,26 @@
             "manage_device_policy_microphone_toggle";
 
     // DPC types
+    private static final int NOT_A_DPC = -1;
     private static final int DEFAULT_DEVICE_OWNER = 0;
     private static final int FINANCED_DEVICE_OWNER = 1;
     private static final int PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE = 2;
     private static final int PROFILE_OWNER_ON_USER_0 = 3;
     private static final int PROFILE_OWNER = 4;
+    private static final int PROFILE_OWNER_ON_USER = 5;
+    private static final int AFFILIATED_PROFILE_OWNER_ON_USER = 6;
+    // DPC types
+    @IntDef(value = {
+            NOT_A_DPC,
+            DEFAULT_DEVICE_OWNER,
+            FINANCED_DEVICE_OWNER,
+            PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
+            PROFILE_OWNER_ON_USER_0,
+            PROFILE_OWNER,
+            PROFILE_OWNER_ON_USER,
+            AFFILIATED_PROFILE_OWNER_ON_USER
+    })
+    private @interface DpcType {}
 
     // Permissions of existing DPC types.
     private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of(
@@ -22632,7 +22769,9 @@
                     SET_TIME_ZONE
             );
 
-
+    /**
+     * All the additional permissions granted to a Profile Owner on user 0.
+     */
     private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
             List.of(
                     MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
@@ -22657,6 +22796,20 @@
             );
 
     /**
+     * All the additional permissions granted to a Profile Owner on an unaffiliated user.
+     */
+    private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_PERMISSIONS =
+            List.of(
+                    MANAGE_DEVICE_POLICY_LOCK_TASK
+            );
+
+    /**
+     * All the additional permissions granted to a Profile Owner on an affiliated user.
+     */
+    private static final List<String> ADDITIONAL_AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS =
+            List.of();
+
+    /**
      * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
      * {@link ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS}.
      */
@@ -22670,6 +22823,20 @@
     private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS  =
             new ArrayList();
 
+    /**
+     * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+     * {@link ADDITIONAL_AFFILIATED_PROFIL_OWNER_ON_USER_PERMISSIONS}.
+     */
+    private static final List<String> AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS =
+            new ArrayList();
+
+    /**
+     * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+     * {@link ADDITIONAL_PROFILE_OWNER_ON_USER_PERMISSIONS}.
+     */
+    private static final List<String> PROFILE_OWNER_ON_USER_PERMISSIONS =
+            new ArrayList();
+
 
     private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
     {
@@ -22682,6 +22849,16 @@
         // some extra permissions.
         PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
         PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS);
+        // Profile owners on users have all the permission of a profile owner plus
+        // some extra permissions.
+        PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
+        PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(
+                ADDITIONAL_PROFILE_OWNER_ON_USER_PERMISSIONS);
+        // Profile owners on affiliated users have all the permission of a profile owner on a user
+        // plus some extra permissions.
+        AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(PROFILE_OWNER_ON_USER_PERMISSIONS);
+        AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(
+                ADDITIONAL_AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS);
 
         DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS);
         DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS);
@@ -22689,6 +22866,9 @@
                 PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS);
         DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS);
         DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS);
+        DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER, PROFILE_OWNER_ON_USER_PERMISSIONS);
+        DPC_PERMISSIONS.put(AFFILIATED_PROFILE_OWNER_ON_USER,
+                AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS);
     }
     //Map of Permission to Delegate Scope.
     private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>();
@@ -23066,22 +23246,9 @@
         if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
             return true;
         }
-        // Check the permissions of DPCs
-        if (isDefaultDeviceOwner(caller)) {
-            return DPC_PERMISSIONS.get(DEFAULT_DEVICE_OWNER).contains(permission);
-        }
-        if (isFinancedDeviceOwner(caller)) {
-            return DPC_PERMISSIONS.get(FINANCED_DEVICE_OWNER).contains(permission);
-        }
-        if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
-            return DPC_PERMISSIONS.get(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE).contains(
-                    permission);
-        }
-        if (isProfileOwnerOnUser0(caller)) {
-            return DPC_PERMISSIONS.get(PROFILE_OWNER_ON_USER_0).contains(permission);
-        }
-        if (isProfileOwner(caller)) {
-            return DPC_PERMISSIONS.get(PROFILE_OWNER).contains(permission);
+        int dpcType = getDpcType(caller);
+        if (dpcType != NOT_A_DPC) {
+            return DPC_PERMISSIONS.get(dpcType).contains(permission);
         }
         // Check the permission for the role-holder
         if (isCallerDevicePolicyManagementRoleHolder(caller)) {
@@ -23151,6 +23318,35 @@
         return calledOnParent ? getProfileParentId(callingUserId) : callingUserId;
     }
 
+    /**
+     * Return the DPC type of the given caller.
+     */
+    private @DpcType int getDpcType(CallerIdentity caller) {
+        // Check the permissions of DPCs
+        if (isDefaultDeviceOwner(caller)) {
+            return DEFAULT_DEVICE_OWNER;
+        }
+        if (isFinancedDeviceOwner(caller)) {
+            return FINANCED_DEVICE_OWNER;
+        }
+        if (isProfileOwner(caller)) {
+            if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+                return PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
+            }
+            if (isManagedProfile(caller.getUserId())) {
+                return PROFILE_OWNER;
+            }
+            if (isProfileOwnerOnUser0(caller)) {
+                return PROFILE_OWNER_ON_USER_0;
+            }
+            if (isUserAffiliatedWithDevice(caller.getUserId())) {
+                return AFFILIATED_PROFILE_OWNER_ON_USER;
+            }
+            return PROFILE_OWNER_ON_USER;
+        }
+        return NOT_A_DPC;
+    }
+
     private boolean isPermissionCheckFlagEnabled() {
         return DeviceConfig.getBoolean(
                 NAMESPACE_DEVICE_POLICY_MANAGER,
@@ -23346,14 +23542,16 @@
         applyManagedSubscriptionsPolicyIfRequired();
 
         int policyType = getManagedSubscriptionsPolicy().getPolicyType();
-        if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
-            final long id = mInjector.binderClearCallingIdentity();
-            try {
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
                 installOemDefaultDialerAndSmsApp(caller.getUserId());
                 updateTelephonyCrossProfileIntentFilters(parentUserId, caller.getUserId(), true);
-            } finally {
-                mInjector.binderRestoreCallingIdentity(id);
+            } else if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS) {
+                updateTelephonyCrossProfileIntentFilters(parentUserId, caller.getUserId(), false);
             }
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 8c2468a..638596b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.admin.AccountTypePolicyKey;
 import android.app.admin.BooleanPolicyValue;
 import android.app.admin.DevicePolicyIdentifiers;
 import android.app.admin.DevicePolicyManager;
@@ -281,6 +282,32 @@
                         DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY, packageName));
     }
 
+    // This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
+    // actual policy with the correct arguments (i.e. packageName) when reading the policies from
+    // xml.
+    static PolicyDefinition<Boolean> GENERIC_ACCOUNT_MANAGEMENT_DISABLED =
+            new PolicyDefinition<>(
+                    new AccountTypePolicyKey(
+                            DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY),
+                    TRUE_MORE_RESTRICTIVE,
+                    POLICY_FLAG_LOCAL_ONLY_POLICY,
+                    // Nothing is enforced, we just need to store it
+                    (Boolean value, Context context, Integer userId, PolicyKey policyKey) -> true,
+                    new BooleanPolicySerializer());
+
+    /**
+     * Passing in {@code null} for {@code accountType} will return
+     * {@link #GENERIC_ACCOUNT_MANAGEMENT_DISABLED}.
+     */
+    static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(String accountType) {
+        if (accountType == null) {
+            return GENERIC_ACCOUNT_MANAGEMENT_DISABLED;
+        }
+        return GENERIC_ACCOUNT_MANAGEMENT_DISABLED.createPolicyDefinition(
+                new AccountTypePolicyKey(
+                        DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY, accountType));
+    }
+
     private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
     private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
 
@@ -304,6 +331,8 @@
                 KEYGUARD_DISABLED_FEATURES);
         POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY,
                 GENERIC_APPLICATION_HIDDEN);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY,
+                GENERIC_ACCOUNT_MANAGEMENT_DISABLED);
 
         // User Restriction Policies
         USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index bb3a476..90691a7 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -34,6 +34,7 @@
 import android.app.ActivityManagerInternal;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.IInputManager;
 import android.hardware.input.InputManagerGlobal;
@@ -121,6 +122,7 @@
     protected IInputMethodInvoker mMockInputMethodInvoker;
     protected InputMethodManagerService mInputMethodManagerService;
     protected ServiceThread mServiceThread;
+    protected boolean mIsLargeScreen;
 
     @BeforeClass
     public static void setupClass() {
@@ -145,6 +147,8 @@
         spyOn(mContext);
 
         mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        mIsLargeScreen = mContext.getResources().getConfiguration()
+                .isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
         mCallingUserId = UserHandle.getCallingUserId();
         mEditorInfo = new EditorInfo();
         mEditorInfo.packageName = TEST_EDITOR_PKG_NAME;
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
index c6b355c..cea65b5 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -124,7 +124,8 @@
 
         switch (mSoftInputState) {
             case SOFT_INPUT_STATE_UNSPECIFIED:
-                boolean showSoftInput = mSoftInputAdjustment == SOFT_INPUT_ADJUST_RESIZE;
+                boolean showSoftInput =
+                        (mSoftInputAdjustment == SOFT_INPUT_ADJUST_RESIZE) || mIsLargeScreen;
                 verifyShowSoftInput(
                         showSoftInput /* setVisible */, showSoftInput /* showSoftInput */);
                 // Soft input was hidden by default, so it doesn't need to call
@@ -165,7 +166,8 @@
 
         switch (mSoftInputState) {
             case SOFT_INPUT_STATE_UNSPECIFIED:
-                boolean hideSoftInput = mSoftInputAdjustment != SOFT_INPUT_ADJUST_RESIZE;
+                boolean hideSoftInput =
+                        (mSoftInputAdjustment != SOFT_INPUT_ADJUST_RESIZE) && !mIsLargeScreen;
                 verifyShowSoftInput(false /* setVisible */, false /* showSoftInput */);
                 // Soft input was hidden by default, so it doesn't need to call
                 // {@code IMS#hideSoftInput()}.
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
index 8c84014..dc92376 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
@@ -199,10 +199,11 @@
         }
 
         public Intent getResult() {
-            try {
-                return mResult.take();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
+            while (true) {
+                try {
+                    return mResult.take();
+                } catch (InterruptedException e) {
+                }
             }
         }
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java
index 07a81ff..c23d4b1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java
@@ -244,6 +244,15 @@
         verifyZeroInteractions(mDisplayPowerCallbacks);
     }
 
+    @Test
+    public void testReleaseAll() throws Exception {
+        // Use WAKE_LOCK_MAX to verify it has been correctly set and used in releaseAll().
+        verifyWakelockAcquisition(WakelockController.WAKE_LOCK_MAX,
+                () -> mWakelockController.hasUnfinishedBusiness());
+        mWakelockController.releaseAll();
+        assertFalse(mWakelockController.hasUnfinishedBusiness());
+    }
+
     private void verifyWakelockAcquisitionAndReaquisition(int wakelockId,
             Callable<Boolean> isWakelockAcquiredCallable)
             throws Exception {
@@ -284,6 +293,4 @@
         assertFalse(mWakelockController.releaseWakelock(wakelockId));
         assertFalse(isWakelockAcquiredCallable.call());
     }
-
-
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 06ba5dd..931a2bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -308,6 +308,7 @@
         whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
         whenever(mocks.systemConfig.defaultVrComponents).thenReturn(ArraySet())
         whenever(mocks.systemConfig.hiddenApiWhitelistedApps).thenReturn(ArraySet())
+        whenever(mocks.systemConfig.appMetadataFilePaths).thenReturn(ArrayMap())
         wheneverStatic { SystemProperties.set(anyString(), anyString()) }.thenDoNothing()
         wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true)
         wheneverStatic { Environment.getApexDirectory() }.thenReturn(apexDirectory)
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index 541b077..a140730 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -21,10 +21,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
@@ -71,9 +75,12 @@
     RollbackInfo mRollbackInfo;
     @Mock
     PackageRollbackInfo mPackageRollbackInfo;
+    @Mock
+    PackageManager mMockPackageManager;
 
     private MockitoSession mSession;
     private static final String APP_A = "com.package.a";
+    private static final String APP_B = "com.package.b";
     private static final long VERSION_CODE = 1L;
     private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
 
@@ -116,7 +123,7 @@
         RollbackPackageHealthObserver observer =
                 spy(new RollbackPackageHealthObserver(mMockContext));
         VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE);
-
+        VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
 
         when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
 
@@ -137,13 +144,16 @@
         assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
                 observer.onHealthCheckFailed(null,
                         PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
-        // non-native crash
+        // non-native crash for the package
         assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
                 observer.onHealthCheckFailed(testFailedPackage,
                         PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
-        // Second non-native crash again
+        // non-native crash for a different package
         assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
-                observer.onHealthCheckFailed(testFailedPackage,
+                observer.onHealthCheckFailed(secondFailedPackage,
+                        PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
+        assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+                observer.onHealthCheckFailed(secondFailedPackage,
                         PackageWatchdog.FAILURE_REASON_APP_CRASH, 2));
         // Subsequent crashes when rollbacks have completed
         when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
@@ -152,6 +162,51 @@
                         PackageWatchdog.FAILURE_REASON_APP_CRASH, 3));
     }
 
+    @Test
+    public void testIsPersistent() {
+        RollbackPackageHealthObserver observer =
+                spy(new RollbackPackageHealthObserver(mMockContext));
+        assertTrue(observer.isPersistent());
+    }
+
+    @Test
+    public void testMayObservePackage_withoutAnyRollback() {
+        RollbackPackageHealthObserver observer =
+                spy(new RollbackPackageHealthObserver(mMockContext));
+        when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
+        assertFalse(observer.mayObservePackage(APP_A));
+    }
+
+    @Test
+    public void testMayObservePackage_forPersistentApp()
+            throws PackageManager.NameNotFoundException {
+        RollbackPackageHealthObserver observer =
+                spy(new RollbackPackageHealthObserver(mMockContext));
+        ApplicationInfo info = new ApplicationInfo();
+        info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM;
+        when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
+        when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockPackageManager.getApplicationInfo(APP_A, 0)).thenReturn(info);
+        assertTrue(observer.mayObservePackage(APP_A));
+    }
+
+    @Test
+    public void testMayObservePackage_forNonPersistentApp()
+            throws PackageManager.NameNotFoundException {
+        RollbackPackageHealthObserver observer =
+                spy(new RollbackPackageHealthObserver(mMockContext));
+        when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
+        when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockPackageManager.getApplicationInfo(APP_A, 0))
+                .thenThrow(new PackageManager.NameNotFoundException());
+        assertFalse(observer.mayObservePackage(APP_A));
+    }
+
     /**
      * Test that isAutomaticRollbackDenied works correctly when packages that are not
      * denied are sent.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 913d8c1..11e4120 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -435,9 +436,9 @@
         mMockConnection.invokeCallbacks();
 
         assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-        verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
-                DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
-                animate, TEST_SERVICE_ID);
+        verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+                eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
+                any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
     }
 
     @Test
@@ -504,6 +505,42 @@
     }
 
     @Test
+    public void configTransitionToFullScreenWithAnimation_windowMagnifying_notifyService()
+            throws RemoteException {
+        final boolean animate = true;
+        activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+        reset(mService);
+        MagnificationConfig config = (new MagnificationConfig.Builder())
+                .setMode(MODE_FULLSCREEN).build();
+        mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+                config, animate, TEST_SERVICE_ID);
+        verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+                /* scale= */ anyFloat(), /* centerX= */ anyFloat(), /* centerY= */ anyFloat(),
+                mCallbackArgumentCaptor.capture(), /* id= */ anyInt());
+        mCallbackArgumentCaptor.getValue().onResult(true);
+        mMockConnection.invokeCallbacks();
+
+        verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void configTransitionToFullScreenWithoutAnimation_windowMagnifying_notifyService()
+            throws RemoteException {
+        final boolean animate = false;
+        activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+        reset(mService);
+        MagnificationConfig config = (new MagnificationConfig.Builder())
+                .setMode(MODE_FULLSCREEN).build();
+        mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+                config, animate, TEST_SERVICE_ID);
+        mMockConnection.invokeCallbacks();
+
+        verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_FULLSCREEN);
+    }
+
+    @Test
     public void interruptDuringTransitionToWindow_disablingFullScreen_discardPreviousTransition()
             throws RemoteException {
         activateMagnifier(MODE_FULLSCREEN, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index 9578993..acdfee9 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
@@ -48,8 +49,10 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -63,8 +66,9 @@
     private AnrHelper mAnrHelper;
 
     private ProcessRecord mAnrApp;
-    private ExecutorService mExecutorService;
+    private ExecutorService mAuxExecutorService;
 
+    private Future<File> mEarlyDumpFuture;
     @Rule
     public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
 
@@ -91,9 +95,12 @@
                         return mServiceThreadRule.getThread().getThreadHandler();
                     }
                 }, mServiceThreadRule.getThread());
-            mExecutorService = mock(ExecutorService.class);
+            mAuxExecutorService = mock(ExecutorService.class);
+            final ExecutorService earlyDumpExecutorService = mock(ExecutorService.class);
+            mEarlyDumpFuture = mock(Future.class);
+            doReturn(mEarlyDumpFuture).when(earlyDumpExecutorService).submit(any(Callable.class));
 
-            mAnrHelper = new AnrHelper(service, mExecutorService);
+            mAnrHelper = new AnrHelper(service, mAuxExecutorService, earlyDumpExecutorService);
         });
     }
 
@@ -125,8 +132,8 @@
 
         verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
                 eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
-                eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mExecutorService),
-                eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/);
+                eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService),
+                eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture));
     }
 
     @Test
@@ -139,7 +146,7 @@
             processingLatch.await();
             return null;
         }).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(),
-                anyBoolean(), any(), any(), anyBoolean(), anyBoolean());
+                anyBoolean(), any(), any(), anyBoolean(), anyBoolean(), any());
         final ApplicationInfo appInfo = new ApplicationInfo();
         final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
                 "annotation");
@@ -162,7 +169,7 @@
         processingLatch.countDown();
         // There is only one ANR reported.
         verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding(
-                anyString(), any(), any(), any(), anyBoolean(), any(), eq(mExecutorService),
-                anyBoolean(), anyBoolean());
+                anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService),
+                anyBoolean(), anyBoolean(), any());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index 6350e22..d92b9f8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -203,6 +203,6 @@
         processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
                 null /* parentShortComponentName */, null /* parentProcess */,
                 false /* aboveSystem */, timeoutRecord, mExecutorService, false /* onlyDumpSelf */,
-                false /*isContinuousAnr*/);
+                false /*isContinuousAnr*/, null);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
index ebf7fd8..2102b09 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
@@ -17,7 +17,8 @@
 package com.android.server.biometrics.sensors;
 
 import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_DEFAULT;
-import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_PERMANENT_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_TIMED_LOCKED;
 import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_UNLOCKED;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -76,7 +77,22 @@
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
                 AUTHENTICATOR_DEFAULT);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
+    }
+
+    @Test
+    public void testConvenientLockoutTimed() {
+        mAuthResultCoordinator.lockOutTimed(
+                BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
+
+        final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+                AUTHENTICATOR_DEFAULT);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+                AUTHENTICATOR_DEFAULT);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+                AUTHENTICATOR_TIMED_LOCKED);
     }
 
     @Test
@@ -97,16 +113,31 @@
     @Test
     public void testWeakLockout() {
         mAuthResultCoordinator.lockedOutFor(
-                BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
+                BiometricManager.Authenticators.BIOMETRIC_WEAK);
 
         Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
 
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
                 AUTHENTICATOR_DEFAULT);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
-                AUTHENTICATOR_DEFAULT);
+                AUTHENTICATOR_PERMANENT_LOCKED);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
+    }
+
+    @Test
+    public void testWeakLockoutTimed() {
+        mAuthResultCoordinator.lockOutTimed(
+                BiometricManager.Authenticators.BIOMETRIC_WEAK);
+
+        Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+                AUTHENTICATOR_DEFAULT);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+                AUTHENTICATOR_TIMED_LOCKED);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+                AUTHENTICATOR_TIMED_LOCKED);
     }
 
     @Test
@@ -132,13 +163,27 @@
         Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
 
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
     }
 
+    @Test
+    public void testStrongLockoutTimed() {
+        mAuthResultCoordinator.lockOutTimed(
+                BiometricManager.Authenticators.BIOMETRIC_STRONG);
+
+        Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+                AUTHENTICATOR_TIMED_LOCKED);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+                AUTHENTICATOR_TIMED_LOCKED);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+                AUTHENTICATOR_TIMED_LOCKED);
+    }
 
     @Test
     public void testStrongUnlock() {
@@ -167,10 +212,30 @@
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
                 AUTHENTICATOR_DEFAULT);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
         assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
-                AUTHENTICATOR_LOCKED);
+                AUTHENTICATOR_PERMANENT_LOCKED);
 
     }
 
+    @Test
+    public void testLockoutAndAuth() {
+        mAuthResultCoordinator.lockedOutFor(
+                BiometricManager.Authenticators.BIOMETRIC_WEAK);
+        mAuthResultCoordinator.authenticatedFor(
+                BiometricManager.Authenticators.BIOMETRIC_STRONG);
+
+        final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                & AUTHENTICATOR_UNLOCKED).isEqualTo(
+                AUTHENTICATOR_UNLOCKED);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+                & AUTHENTICATOR_UNLOCKED).isEqualTo(
+                AUTHENTICATOR_UNLOCKED);
+        assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)
+                & AUTHENTICATOR_UNLOCKED).isEqualTo(
+                AUTHENTICATOR_UNLOCKED);
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
index c3b9cb1..f26c7e6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
@@ -154,7 +154,7 @@
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
-        assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+        assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
     }
 
@@ -194,7 +194,7 @@
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
-        assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+        assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
 
         assertThat(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
index 968844e4c..c28de55 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
@@ -49,7 +49,7 @@
     private Clock mClock;
 
     private static void unlockAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
-        lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, true /* canAuthenticate */);
+        lockoutState.clearPermanentLockOut(userId, BIOMETRIC_STRONG);
         assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_WEAK)).isEqualTo(
@@ -59,7 +59,7 @@
     }
 
     private static void lockoutAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
-        lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, false /* canAuthenticate */);
+        lockoutState.setPermanentLockOut(userId, BIOMETRIC_STRONG);
         assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_PERMANENT);
         assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_WEAK)).isEqualTo(
@@ -96,8 +96,7 @@
     @Test
     public void testConvenienceLockout() {
         unlockAllBiometrics();
-        mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
-                false /* canAuthenticate */);
+        mLockoutState.setPermanentLockOut(PRIMARY_USER, BIOMETRIC_CONVENIENCE);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -110,7 +109,7 @@
     @Test
     public void testWeakLockout() {
         unlockAllBiometrics();
-        mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, false /* canAuthenticate */);
+        mLockoutState.setPermanentLockOut(PRIMARY_USER, BIOMETRIC_WEAK);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -123,8 +122,7 @@
     @Test
     public void testStrongLockout() {
         lockoutAllBiometrics();
-        mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
-                false /* canAuthenticate */);
+        mLockoutState.setPermanentLockOut(PRIMARY_USER, BIOMETRIC_STRONG);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_PERMANENT);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -137,8 +135,7 @@
     @Test
     public void testConvenienceUnlock() {
         lockoutAllBiometrics();
-        mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
-                true /* canAuthenticate */);
+        mLockoutState.clearPermanentLockOut(PRIMARY_USER, BIOMETRIC_CONVENIENCE);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_PERMANENT);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -150,7 +147,7 @@
     @Test
     public void testWeakUnlock() {
         lockoutAllBiometrics();
-        mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, true /* canAuthenticate */);
+        mLockoutState.clearPermanentLockOut(PRIMARY_USER, BIOMETRIC_WEAK);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_PERMANENT);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -162,8 +159,7 @@
     @Test
     public void testStrongUnlock() {
         lockoutAllBiometrics();
-        mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
-                true /* canAuthenticate */);
+        mLockoutState.clearPermanentLockOut(PRIMARY_USER, BIOMETRIC_STRONG);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -180,7 +176,7 @@
         lockoutAllBiometrics(lockoutState, userOne);
         lockoutAllBiometrics(lockoutState, userTwo);
 
-        lockoutState.setAuthenticatorTo(userOne, BIOMETRIC_WEAK, true /* canAuthenticate */);
+        lockoutState.clearPermanentLockOut(userOne, BIOMETRIC_WEAK);
         assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_PERMANENT);
         assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_WEAK)).isEqualTo(
@@ -205,8 +201,7 @@
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
 
-        mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG,
-                System.currentTimeMillis() + 1);
+        mLockoutState.setTimedLockout(PRIMARY_USER, BIOMETRIC_STRONG);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_TIMED);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -225,8 +220,7 @@
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
 
-        when(mClock.millis()).thenReturn(0L);
-        mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG, 1);
+        mLockoutState.setTimedLockout(PRIMARY_USER, BIOMETRIC_STRONG);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_TIMED);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -235,7 +229,7 @@
                 mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
                 LockoutTracker.LOCKOUT_TIMED);
 
-        when(mClock.millis()).thenReturn(2L);
+        mLockoutState.clearTimedLockout(PRIMARY_USER, BIOMETRIC_STRONG);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
                 LockoutTracker.LOCKOUT_NONE);
         assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 1f29bec..9c9d3f8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -19,6 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
@@ -109,7 +111,8 @@
                 mUserSwitchCallback);
         mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
                 TAG, mScheduler, SENSOR_ID,
-                USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+                USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+                mHalSessionCallback);
 
         final SensorProps sensor1 = new SensorProps();
         sensor1.commonProps = new CommonProps();
@@ -164,5 +167,6 @@
     private void verifyNotLocked() {
         assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
         verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+        verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 7ae4e17..0c13466 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
@@ -97,7 +99,8 @@
                 mUserSwitchCallback);
         mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
                 TAG, mScheduler, SENSOR_ID,
-                USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+                USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+                mHalSessionCallback);
     }
 
     @Test
@@ -130,5 +133,6 @@
     private void verifyNotLocked() {
         assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
         verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+        verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index e396263..728606f 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -17,8 +17,13 @@
 package com.android.server.devicestate;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -41,6 +46,8 @@
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
 
+import java.util.Locale;
+
 /**
  * Unit tests for {@link DeviceStateNotificationController}.
  * <p/>
@@ -77,6 +84,8 @@
             Notification.class);
     private final NotificationManager mNotificationManager = mock(NotificationManager.class);
 
+    private DeviceStateNotificationController.NotificationInfoProvider mNotificationInfoProvider;
+
     @Before
     public void setup() throws Exception {
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
@@ -97,6 +106,11 @@
                         THERMAL_TITLE_2, THERMAL_CONTENT_2,
                         POWER_SAVE_TITLE_2, POWER_SAVE_CONTENT_2));
 
+        mNotificationInfoProvider =
+                new DeviceStateNotificationController.NotificationInfoProvider(context);
+        mNotificationInfoProvider = spy(mNotificationInfoProvider);
+        doReturn(notificationInfos).when(mNotificationInfoProvider).loadNotificationInfos();
+
         when(packageManager.getNameForUid(VALID_APP_UID)).thenReturn(VALID_APP_NAME);
         when(packageManager.getNameForUid(INVALID_APP_UID)).thenReturn(INVALID_APP_NAME);
         when(packageManager.getApplicationInfo(eq(VALID_APP_NAME), ArgumentMatchers.any()))
@@ -106,7 +120,7 @@
         when(applicationInfo.loadLabel(eq(packageManager))).thenReturn(VALID_APP_LABEL);
 
         mController = new DeviceStateNotificationController(
-                context, handler, cancelStateRunnable, notificationInfos,
+                context, handler, cancelStateRunnable, mNotificationInfoProvider,
                 packageManager, mNotificationManager);
     }
 
@@ -223,4 +237,26 @@
                 eq(DeviceStateNotificationController.NOTIFICATION_ID),
                 mNotificationCaptor.capture());
     }
+
+    @Test
+    public void test_notificationInfoProvider() {
+        assertNull(mNotificationInfoProvider.getCachedLocale());
+
+        mNotificationInfoProvider.getNotificationInfos(Locale.ENGLISH);
+        verify(mNotificationInfoProvider).refreshNotificationInfos(eq(Locale.ENGLISH));
+        assertEquals(Locale.ENGLISH, mNotificationInfoProvider.getCachedLocale());
+        clearInvocations(mNotificationInfoProvider);
+
+        // If the same locale is used again, the provider uses the cached value, so it won't refresh
+        mNotificationInfoProvider.getNotificationInfos(Locale.ENGLISH);
+        verify(mNotificationInfoProvider, never()).refreshNotificationInfos(eq(Locale.ENGLISH));
+        assertEquals(Locale.ENGLISH, mNotificationInfoProvider.getCachedLocale());
+        clearInvocations(mNotificationInfoProvider);
+
+        // If a different locale is used, the provider refreshes.
+        mNotificationInfoProvider.getNotificationInfos(Locale.ITALY);
+        verify(mNotificationInfoProvider).refreshNotificationInfos(eq(Locale.ITALY));
+        assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale());
+        clearInvocations(mNotificationInfoProvider);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index ad63da5..e960e99 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -16,9 +16,6 @@
 
 package com.android.server.locksettings;
 
-import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG;
-import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER;
-
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -50,7 +47,6 @@
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.security.KeyStore;
 
@@ -235,9 +231,6 @@
         // Adding a fake Device Owner app which will enable escrow token support in LSS.
         when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
                 new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
-        // TODO(b/258213147): Remove
-        DeviceConfig.setProperty(NAMESPACE_DEVICE_POLICY_MANAGER,
-                DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG, "true", /* makeDefault= */ false);
         when(mUserManagerInternal.isDeviceManaged()).thenReturn(true);
         when(mDeviceStateCache.isUserOrganizationManaged(anyInt())).thenReturn(true);
         when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 3f3b8d7..36dc6c5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -87,7 +87,10 @@
     PersistentDataBlockManagerInternal getPersistentDataBlockManager() {
         return mPersistentDataBlockManager;
     }
-
+    @Override
+    public boolean isAutoPinConfirmSettingEnabled(int userId) {
+        return true;
+    }
     private File remapToStorageDir(File origPath) {
         File mappedPath = new File(mStorageDir, origPath.toString());
         mappedPath.getParentFile().mkdirs();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index e8ef398..a48d2cc 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -113,6 +113,11 @@
     }
 
     @Override
+    public boolean isAutoPinConfirmationFeatureAvailable() {
+        return true;
+    }
+
+    @Override
     protected IWeaver getWeaverHidlService() throws RemoteException {
         return mWeaverService;
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index bdc5be6..bfb6b0f1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -607,6 +607,24 @@
     }
 
     @Test
+    public void testStorePinLengthOnDisk() {
+        int userId = 1;
+        LockscreenCredential lockscreenCredentialPin = LockscreenCredential.createPin("123456");
+        MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
+                mGateKeeperService, mUserManager, mPasswordSlotManager);
+        SyntheticPassword sp = manager.newSyntheticPassword(userId);
+        long protectorId = manager.createLskfBasedProtector(mGateKeeperService,
+                lockscreenCredentialPin, sp,
+                userId);
+        PasswordMetrics passwordMetrics =
+                PasswordMetrics.computeForCredential(lockscreenCredentialPin);
+        boolean result = manager.refreshPinLengthOnDisk(passwordMetrics, protectorId, userId);
+
+        assertEquals(manager.getPinLength(protectorId, userId), lockscreenCredentialPin.size());
+        assertTrue(result);
+    }
+
+    @Test
     public void testPasswordDataV2VersionCredentialTypePin_deserialize() {
         // Test that we can deserialize existing PasswordData and don't inadvertently change the
         // wire format.
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index d50aca9..2efd9fc 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -618,13 +618,19 @@
     }
 
     @Test
-    public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() {
+    public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForUsagesInAllowlist() {
         long vibrateStartTime = 100;
         mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN);
 
+        Set<Integer> expectedAllowedVibrations = new HashSet<>(Arrays.asList(
+                USAGE_TOUCH,
+                USAGE_ACCESSIBILITY,
+                USAGE_PHYSICAL_EMULATION,
+                USAGE_HARDWARE_FEEDBACK
+        ));
+
         for (int usage : ALL_USAGES) {
-            if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
-                    || usage == USAGE_PHYSICAL_EMULATION) {
+            if (expectedAllowedVibrations.contains(usage)) {
                 assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                         createCallerInfo(/* uid= */ 0, "", usage), vibrateStartTime));
             } else {
@@ -635,13 +641,19 @@
     }
 
     @Test
-    public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() {
+    public void shouldCancelVibrationOnScreenOff_withSystemUid__returnsFalseForUsagesInAllowlist() {
         long vibrateStartTime = 100;
         mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD);
 
+        Set<Integer> expectedAllowedVibrations = new HashSet<>(Arrays.asList(
+                USAGE_TOUCH,
+                USAGE_ACCESSIBILITY,
+                USAGE_PHYSICAL_EMULATION,
+                USAGE_HARDWARE_FEEDBACK
+        ));
+
         for (int usage : ALL_USAGES) {
-            if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
-                    || usage == USAGE_PHYSICAL_EMULATION) {
+            if (expectedAllowedVibrations.contains(usage)) {
                 assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                         createCallerInfo(Process.SYSTEM_UID, "", usage), vibrateStartTime));
             } else {
@@ -652,13 +664,19 @@
     }
 
     @Test
-    public void shouldCancelVibrationOnScreenOff_withSysUiPkg_returnsFalseForTouchAndHardware() {
+    public void shouldCancelVibrationOnScreenOff_withSysUiPkg_returnsFalseForUsagesInAllowlist() {
         long vibrateStartTime = 100;
         mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_HDMI);
 
+        Set<Integer> expectedAllowedVibrations = new HashSet<>(Arrays.asList(
+                USAGE_TOUCH,
+                USAGE_ACCESSIBILITY,
+                USAGE_PHYSICAL_EMULATION,
+                USAGE_HARDWARE_FEEDBACK
+        ));
+
         for (int usage : ALL_USAGES) {
-            if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
-                    || usage == USAGE_PHYSICAL_EMULATION) {
+            if (expectedAllowedVibrations.contains(usage)) {
                 assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                         createCallerInfo(UID, SYSUI_PACKAGE_NAME, usage), vibrateStartTime));
             } else {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 7d16fb2..4890f3e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -44,11 +44,13 @@
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
@@ -445,12 +447,15 @@
     }
 
     @Test
-    public void testSandboxServiceInterceptionHappensToSandboxedActivityAction()
-            throws InterruptedException {
-
+    public void testSandboxServiceInterceptionHappensToIntentWithSandboxActivityAction() {
         ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
         mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
 
+        PackageManager packageManagerMock = mock(PackageManager.class);
+        String sandboxPackageNameMock = "com.sandbox.mock";
+        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
         Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
         mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
 
@@ -459,13 +464,68 @@
     }
 
     @Test
-    public void testSandboxServiceInterceptionNotCalledForNotSandboxedActivityAction() {
+    public void testSandboxServiceInterceptionHappensToIntentWithSandboxPackage() {
         ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
         mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
 
+        PackageManager packageManagerMock = mock(PackageManager.class);
+        String sandboxPackageNameMock = "com.sandbox.mock";
+        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
+        Intent intent = new Intent().setPackage(sandboxPackageNameMock);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+        verify(spyCallback, times(1)).onInterceptActivityLaunch(
+                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
+    }
+
+    @Test
+    public void testSandboxServiceInterceptionHappensToIntentWithComponentNameWithSandboxPackage() {
+        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
+        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+
+        PackageManager packageManagerMock = mock(PackageManager.class);
+        String sandboxPackageNameMock = "com.sandbox.mock";
+        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
+        Intent intent = new Intent().setComponent(new ComponentName(sandboxPackageNameMock, ""));
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+        verify(spyCallback, times(1)).onInterceptActivityLaunch(
+                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
+    }
+
+    @Test
+    public void testSandboxServiceInterceptionNotCalledWhenIntentNotRelatedToSandbox() {
+        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
+        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+
+        PackageManager packageManagerMock = mock(PackageManager.class);
+        String sandboxPackageNameMock = "com.sandbox.mock";
+        when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+        when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
+        // Intent: null
+        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
+
+        // Action: null, Package: null, ComponentName: null
         Intent intent = new Intent();
         mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
 
+        // Wrong Action
+        intent = new Intent().setAction(Intent.ACTION_VIEW);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+        // Wrong Package
+        intent = new Intent().setPackage("Random");
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+        // Wrong ComponentName's package
+        intent = new Intent().setComponent(new ComponentName("Random", ""));
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
         verify(spyCallback, never()).onInterceptActivityLaunch(
                 any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index 2686a24..d2f0385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -112,13 +112,6 @@
     }
 
     @Test
-    public void testRegisterOrganizer_alreadyRegisteredFeature() {
-        registerMockOrganizer(FEATURE_VENDOR_FIRST);
-        assertThrows(IllegalStateException.class,
-                () -> registerMockOrganizer(FEATURE_VENDOR_FIRST));
-    }
-
-    @Test
     public void testRegisterOrganizer_ignoreUntrustedDisplay() throws RemoteException {
         doReturn(false).when(mDisplayContent).isTrusted();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 10540dc..1ad04a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -619,19 +619,6 @@
     }
 
     @Test
-    public void testRegisterSameFeatureOrganizer_expectThrowsException() {
-        final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
-        final IBinder binder = mock(IBinder.class);
-        doReturn(true).when(binder).isBinderAlive();
-        doReturn(binder).when(mockDisplayAreaOrganizer).asBinder();
-        final DisplayAreaOrganizerController controller =
-                mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
-        controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST);
-        assertThrows(IllegalStateException.class,
-                () -> controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST));
-    }
-
-    @Test
     public void testRegisterUnregisterOrganizer() {
         final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
         doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 5e513f1..3934b02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -94,6 +94,7 @@
         mProvider.setWindowContainer(statusBar,
                 (displayFrames, windowState, rect) -> {
                     rect.set(10, 10, 20, 20);
+                    return 0;
                 }, null);
         mProvider.updateSourceFrame(statusBar.getFrame());
         mProvider.onPostLayout();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index ff2944a..d1d83f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -52,7 +52,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -289,10 +289,12 @@
 
         InsetsSourceProvider statusBarProvider =
                 getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars());
-        final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> imeOverrideProviders =
-                new SparseArray<>();
-        imeOverrideProviders.put(TYPE_INPUT_METHOD, ((displayFrames, windowState, rect) ->
-                rect.set(0, 1, 2, 3)));
+        final SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>>
+                imeOverrideProviders = new SparseArray<>();
+        imeOverrideProviders.put(TYPE_INPUT_METHOD, ((displayFrames, windowState, rect) -> {
+            rect.set(0, 1, 2, 3);
+            return 0;
+        }));
         statusBarProvider.setWindowContainer(statusBar, null, imeOverrideProviders);
         getController().getOrCreateSourceProvider(ID_IME, ime())
                 .setWindowContainer(ime, null, null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index fdb3502..766e74c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -43,6 +43,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -969,6 +971,33 @@
         assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
     }
 
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
+    @Test
+    public void testNeedsRelativeLayeringToIme_systemDialog() {
+        WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
+                mDisplayContent,
+                "SystemDialog", true);
+        mDisplayContent.setImeLayeringTarget(mAppWindow);
+        mAppWindow.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        makeWindowVisible(mImeWindow);
+        systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+        assertTrue(systemDialogWindow.needsRelativeLayeringToIme());
+    }
+
+    @UseTestDisplay(addWindows = {W_INPUT_METHOD})
+    @Test
+    public void testNeedsRelativeLayeringToIme_notificationShadeShouldNotHideSystemDialog() {
+        WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
+                mDisplayContent,
+                "SystemDialog", true);
+        mDisplayContent.setImeLayeringTarget(systemDialogWindow);
+        makeWindowVisible(mImeWindow);
+        WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE,
+                mDisplayContent, "NotificationShade", true);
+        notificationShade.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+        assertFalse(notificationShade.needsRelativeLayeringToIme());
+    }
+
     @Test
     public void testSetFreezeInsetsState() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 74ba45c..3ec6f42 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -31,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
@@ -553,6 +555,30 @@
     }
 
     @Test
+    public void testSystemDialogWindow_expectHigherThanIme_inMultiWindow() {
+        // Simulate the app window is in multi windowing mode and being IME target
+        mAppWindow.getConfiguration().windowConfiguration.setWindowingMode(
+                WINDOWING_MODE_MULTI_WINDOW);
+        mDisplayContent.setImeLayeringTarget(mAppWindow);
+        mDisplayContent.setImeInputTarget(mAppWindow);
+        makeWindowVisible(mImeWindow);
+
+        // Create a popupWindow
+        final WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
+                mDisplayContent, "SystemDialog", true);
+        systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+        spyOn(systemDialogWindow);
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Verify the surface layer of the popupWindow should higher than IME
+        verify(systemDialogWindow).needsRelativeLayeringToIme();
+        assertThat(systemDialogWindow.needsRelativeLayeringToIme()).isTrue();
+        assertZOrderGreaterThan(mTransaction, systemDialogWindow.getSurfaceControl(),
+                mDisplayContent.getImeContainer().getSurfaceControl());
+    }
+
+    @Test
     public void testImeScreenshotLayer() {
         final Task task = createTask(mDisplayContent);
         final WindowState imeAppTarget = createAppWindow(task, TYPE_APPLICATION, "imeAppTarget");
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 1bbea89..790be8d 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -73,7 +73,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -342,21 +341,6 @@
         }
 
         @Override
-        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
-                throws RemoteException {
-            try {
-                return super.onTransact(code, data, reply, flags);
-            } catch (RuntimeException e) {
-                // The activity manager only throws security exceptions, so let's
-                // log all others.
-                if (!(e instanceof SecurityException)) {
-                    Slog.wtf(TAG, "SoundTriggerService Crash", e);
-                }
-                throw e;
-            }
-        }
-
-        @Override
         public int startRecognition(GenericSoundModel soundModel,
                 IRecognitionStatusCallback callback,
                 RecognitionConfig config, boolean runInBatterySaverMode) {
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteGateway.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteGateway.aidl
new file mode 100644
index 0000000..4f1a136
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteGateway.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+oneway interface ISatelliteGateway {
+    // An empty service because Telephony does not need to use any APIs from this service.
+    // Once satellite modem is enabled, Telephony will bind to the ISatelliteGateway service; and
+    // when satellite modem is disabled, Telephony will unbind to the service.
+}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteGatewayService.java b/telephony/java/android/telephony/satellite/stub/SatelliteGatewayService.java
new file mode 100644
index 0000000..f4514a6
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteGatewayService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.android.telephony.Rlog;
+
+/**
+ * Main SatelliteGatewayService implementation, which binds via the Telephony SatelliteController.
+ * Services that extend SatelliteGatewayService must register the service in their AndroidManifest
+ * to be detected by the framework. The application must declare that they require the
+ * "android.permission.BIND_SATELLITE_GATEWAY_SERVICE" permission to ensure that nothing else can
+ * bind to their service except the Telephony framework. The SatelliteGatewayService definition in
+ * the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgSatelliteGatewayService"
+ *     android:permission="android.permission.BIND_SATELLITE_GATEWAY_SERVICE" >
+ *     ...
+ *     <intent-filter>
+ *         <action android:name="android.telephony.satellite.SatelliteGatewayService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the SatelliteGatewayService defined in the manifest if
+ * it is the default SatelliteGatewayService defined in the device overlay
+ * "config_satellite_gateway_service_package".
+ * @hide
+ */
+public abstract class SatelliteGatewayService extends Service {
+    private static final String TAG = "SatelliteGatewayService";
+
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.telephony.satellite.SatelliteGatewayService";
+
+    private final IBinder mBinder = new ISatelliteGateway.Stub() {};
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            Rlog.d(TAG, "SatelliteGatewayService bound");
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * @return The binder for the ISatelliteGateway.
+     * @hide
+     */
+    public final IBinder getBinder() {
+        return mBinder;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ee9d6c1..282b64d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2991,6 +2991,15 @@
     boolean setSatelliteServicePackageName(in String servicePackageName);
 
     /**
+     * This API can be used by only CTS to update satellite gateway service package name.
+     *
+     * @param servicePackageName The package name of the satellite gateway service.
+     * @return {@code true} if the satellite gateway service is set successfully,
+     * {@code false} otherwise.
+     */
+    boolean setSatelliteGatewayServicePackageName(in String servicePackageName);
+
+    /**
      * This API can be used by only CTS to update the timeout duration in milliseconds that
      * satellite should stay at listening mode to wait for the next incoming page before disabling
      * listening mode.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 1b4d6ad..10ce5ea 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.datatypes.component.ComponentNameMatcher
@@ -80,7 +79,7 @@
         flicker.navBarLayerPositionAtStartAndEnd()
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerPositionAtStartAndEndLandscapeOrSeascapeAtStart() {
         Assume.assumeFalse(flicker.scenario.isTablet)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 3a8db45..3f87aef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -15,7 +15,7 @@
  */
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
@@ -60,19 +60,19 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerVisibleStart() {
         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.IME) }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerExistsEnd() {
         flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerVisibleRegionKeepsTheSame() {
         var imeLayerVisibleRegionBeforeTransition: RegionSubject? = null
@@ -85,7 +85,7 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun appWindowWithLetterboxCoversExactlyOnScreen() {
         val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index 12c0874..b1a267a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -16,12 +16,9 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
 import android.platform.test.rule.SettingOverrideRule
 import android.provider.Settings
-import android.tools.common.datatypes.component.ComponentNameMatcher
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -68,12 +65,6 @@
         }
 
     /** {@inheritDoc} */
-    @FlakyTest(bugId = 203538234)
-    @Test
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    /** {@inheritDoc} */
     @Test @Ignore("Display is off at the start") override fun navBarLayerPositionAtStartAndEnd() {}
 
     /** {@inheritDoc} */
@@ -89,6 +80,11 @@
     /** {@inheritDoc} */
     @Test
     @Ignore("Display is off at the start")
+    override fun taskBarWindowIsAlwaysVisible() {}
+
+    /** {@inheritDoc} */
+    @Test
+    @Ignore("Display is off at the start")
     override fun statusBarLayerIsVisibleAtStartAndEnd() =
         super.statusBarLayerIsVisibleAtStartAndEnd()
 
@@ -97,14 +93,6 @@
     @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
     override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
 
-    /**
-     * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
-     * transition
-     */
-    @Presubmit
-    @Test
-    override fun statusBarLayerPositionAtEnd() = super.statusBarLayerPositionAtEnd()
-
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
@@ -143,7 +131,7 @@
         val disableUnseenNotifFilterRule =
             SettingOverrideRule(
                 Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                /* value= */ "0",
+                /* value = */ "0",
             )
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index 222caed..e414325 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -97,6 +97,11 @@
 
     /** {@inheritDoc} */
     @Test
+    @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+    override fun taskBarWindowIsAlwaysVisible() {}
+
+    /** {@inheritDoc} */
+    @Test
     @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
     override fun statusBarLayerPositionAtStartAndEnd() {}
 
@@ -128,9 +133,6 @@
     override fun navBarWindowIsAlwaysVisible() {}
 
     /** {@inheritDoc} */
-    @FlakyTest @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
-
-    /** {@inheritDoc} */
     @FlakyTest(bugId = 246284526)
     @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 4a9507a..23cb1d4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -191,12 +191,6 @@
     @Postsubmit
     override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
 
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 9ab6156..17f5638 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.common.Rotation
@@ -70,7 +69,7 @@
      * Checks that the [ComponentNameMatcher.NAV_BAR] layer starts invisible, becomes visible during
      * unlocking animation and remains visible at the end
      */
-    @FlakyTest(bugId = 227083463)
+    @Presubmit
     @Test
     fun navBarLayerVisibilityChanges() {
         Assume.assumeFalse(flicker.scenario.isTablet)
@@ -155,8 +154,19 @@
     @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
     override fun statusBarWindowIsAlwaysVisible() {}
 
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun appWindowBecomesFirstAndOnlyTopWindow() =
+        super.appWindowBecomesFirstAndOnlyTopWindow()
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
     /** Checks the [ComponentNameMatcher.NAV_BAR] is visible at the end of the transition */
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsVisibleAtEnd() {
         Assume.assumeFalse(flicker.scenario.isTablet)
@@ -185,18 +195,11 @@
         super.appLayerBecomesVisible()
     }
 
-    /** {@inheritDoc} */
-    @FlakyTest @Test override fun entireScreenCovered() = super.entireScreenCovered()
-
-    @FlakyTest(bugId = 218470989)
+    @Presubmit
     @Test
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
-    @FlakyTest(bugId = 227143265)
-    @Test
-    override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
-
     @FlakyTest(bugId = 251217585)
     @Test
     override fun focusChanges() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 786bb32..18e49fe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -24,11 +24,12 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
 import android.view.KeyEvent
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.helpers.setRotation
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
 import org.junit.FixMethodOrder
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -44,6 +45,7 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] and wait animation to complete
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -112,16 +114,16 @@
     @Test
     override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Ignore("Status bar visibility depends on whether the permission dialog is displayed or not")
     @Test
     override fun statusBarLayerIsVisibleAtStartAndEnd() =
         super.statusBarLayerIsVisibleAtStartAndEnd()
 
-    @Postsubmit
+    @Ignore("Status bar visibility depends on whether the permission dialog is displayed or not")
     @Test
     override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
 
-    @Postsubmit
+    @Ignore("Status bar visibility depends on whether the permission dialog is displayed or not")
     @Test
     override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
index 0c7e452..9c70e6e 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
@@ -32,6 +32,7 @@
 
 import android.app.Instrumentation;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.SystemClock;
 import android.platform.test.annotations.RootPermissionTest;
 import android.platform.test.rule.UnlockScreenRule;
@@ -69,8 +70,6 @@
             new PressHomeBeforeTestRule();
     @Rule(order = 4) public ScreenCaptureRule mScreenCaptureRule =
             new ScreenCaptureRule("/sdcard/InputMethodStressTest");
-
-    // TODO(b/240359838): add test case {@code Configuration.SCREENLAYOUT_SIZE_LARGE}.
     @Parameterized.Parameters(
             name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}")
     public static List<Object[]> windowAndSoftInputFlagParameters() {
@@ -80,11 +79,14 @@
     private final int mSoftInputFlags;
     private final int mWindowFocusFlags;
     private final Instrumentation mInstrumentation;
+    private final boolean mIsLargeScreen;
 
     public AutoShowTest(int windowFocusFlags, int softInputVisibility, int softInputAdjustment) {
         mSoftInputFlags = softInputVisibility | softInputAdjustment;
         mWindowFocusFlags = windowFocusFlags;
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mIsLargeScreen = mInstrumentation.getContext().getResources()
+                .getConfiguration().isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
     }
 
     /**
@@ -322,8 +324,7 @@
         verifyClickBehavior(activity);
     }
 
-    public static void verifyAutoShowBehavior_forwardWithKeyboardOff(TestActivity activity) {
-        // public: also used by ImeOpenCloseStressTest
+    private void verifyAutoShowBehavior_forwardWithKeyboardOff(TestActivity activity) {
         if (hasUnfocusableWindowFlags(activity)) {
             verifyImeAlwaysHiddenWithWindowFlagSet(activity);
             return;
@@ -353,12 +354,12 @@
                 break;
             }
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: {
-                if (softInputAdjustment
-                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
+                if ((softInputAdjustment
+                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) || mIsLargeScreen) {
                     // The current system behavior will choose to show IME automatically when
                     // navigating forward to an app that has no visibility state specified
                     // (i.e. SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE
-                    // flag.
+                    // flag or running on a large screen device.
                     waitOnMainUntilImeIsShown(editText);
                 } else {
                     verifyImeIsAlwaysHidden(editText);
@@ -370,7 +371,7 @@
         }
     }
 
-    private static void verifyAutoShowBehavior_forwardWithKeyboardOn(TestActivity activity) {
+    private void verifyAutoShowBehavior_forwardWithKeyboardOn(TestActivity activity) {
         int windowFlags = activity.getWindow().getAttributes().flags;
         int softInputMode = activity.getWindow().getAttributes().softInputMode;
         int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
@@ -414,12 +415,12 @@
                 break;
             }
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: {
-                if (softInputAdjustment
-                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
+                if ((softInputAdjustment
+                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) || mIsLargeScreen) {
                     // The current system behavior will choose to show IME automatically when
-                    // navigating
-                    // forward to an app that has no visibility state specified  (i.e.
-                    // SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE flag.
+                    // navigating forward to an app that has no visibility state specified  (i.e.
+                    // SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE flag or
+                    // running on a large screen device.
                     waitOnMainUntilImeIsShown(editText);
                 } else {
                     verifyImeIsAlwaysHidden(editText);
diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml
index 947453d..ad05847 100644
--- a/tests/SharedLibraryLoadingTest/AndroidTest.xml
+++ b/tests/SharedLibraryLoadingTest/AndroidTest.xml
@@ -22,7 +22,6 @@
 
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-        <option name="cleanup" value="false" />
         <option name="remount-system" value="true" />
         <option name="push"
                 value="SharedLibraryLoadingTests_StandardSharedLibrary.apk->/product/app/SharedLibraryLoadingTests_StandardSharedLibrary.apk" />
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
index 166fbdd..c6e675a 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
@@ -84,6 +84,12 @@
     public @interface DeviceType {
     }
 
+    /**
+     * Key in extras bundle indicating that the device battery is charging.
+     * @hide
+     */
+    public static final String EXTRA_KEY_IS_BATTERY_CHARGING = "is_battery_charging";
+
     @DeviceType
     private final int mDeviceType;
     private final String mDeviceName;