Merge "Bump CLOCK_VIBRANT min chroma to 70" 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/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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3312294..9e59ee4 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2547,7 +2547,7 @@
             .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
         new AppOpInfo.Builder(OP_TURN_SCREEN_ON, OPSTR_TURN_SCREEN_ON, "TURN_SCREEN_ON")
             .setPermission(Manifest.permission.TURN_SCREEN_ON)
-            .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+            .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
         new AppOpInfo.Builder(OP_GET_ACCOUNTS, OPSTR_GET_ACCOUNTS, "GET_ACCOUNTS")
             .setPermission(Manifest.permission.GET_ACCOUNTS)
             .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 63da0a2..d375760 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2916,6 +2916,14 @@
             }
         }
 
+        if (isStyle(CallStyle.class) & extras != null) {
+            Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON);
+            if (callPerson != null) {
+                visitor.accept(callPerson.getIconUri());
+            }
+            visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON));
+        }
+
         if (mBubbleMetadata != null) {
             visitIconUri(visitor, mBubbleMetadata.getIcon());
         }
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..307f306 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;
@@ -12348,7 +12349,9 @@
                             null, new String[] { getType() },
                             new ClipData.Item(text, htmlText, null, stream));
                     setClipData(clipData);
-                    addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    if (stream != null) {
+                        addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    }
                     return true;
                 }
             } catch (ClassCastException e) {
@@ -12387,7 +12390,9 @@
                     }
 
                     setClipData(clipData);
-                    addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    if (streams != null) {
+                        addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    }
                     return true;
                 }
             } catch (ClassCastException e) {
@@ -12463,4 +12468,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/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 048289f..960d10a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,6 +2628,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                targetCode)) {
+            Slog.w(TAG, "Package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + targetCode
@@ -2699,6 +2708,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                minCode)) {
+            Slog.w(TAG, "Package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 3e1c5bb..8cc4cdb 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,6 +316,15 @@
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        minCode)) {
+            Slog.w(TAG, "Parsed package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -368,19 +377,27 @@
             return input.success(targetVers);
         }
 
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        }
+
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        targetCode)) {
+            Slog.w(TAG, "Parsed package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         try {
             if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
                 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
             }
         } catch (IllegalArgumentException e) {
-            // isAtMost() throws it when encountering an older SDK codename
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
-        }
-
-        // If it's a pre-release SDK and the codename matches this platform, it
-        // definitely targets this SDK.
-        if (matchTargetCode(platformSdkCodenames, targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
         }
 
         // Otherwise, we're looking at an incompatible pre-release SDK.
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/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/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/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index a5e7086..b65c1a1 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -845,11 +845,7 @@
 
                     // Calling overScrollBy will call onOverScrolled, which
                     // calls onScrollChanged if applicable.
-                    if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)
-                            && !hasNestedScrollingParent()) {
-                        // Break our velocity if we hit a scroll barrier.
-                        mVelocityTracker.clear();
-                    }
+                    overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true);
 
                     final int scrolledDeltaY = mScrollY - oldY;
                     final int unconsumedY = deltaY - scrolledDeltaY;
@@ -894,6 +890,7 @@
 
                     mActivePointerId = INVALID_POINTER;
                     endDrag();
+                    velocityTracker.clear();
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
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/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index e0ee683..e44f436 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -94,26 +94,29 @@
     @Nullable
     private final IOnBackInvokedCallback mOnBackInvokedCallback;
     private final boolean mPrepareRemoteAnimation;
+    private final boolean mAnimationCallback;
     @Nullable
     private final CustomAnimationInfo mCustomAnimationInfo;
 
     /**
      * Create a new {@link BackNavigationInfo} instance.
      *
-     * @param type                    The {@link BackTargetType} of the destination (what will be
-     * @param onBackNavigationDone    The callback to be called once the client is done with the
-     *                                back preview.
-     * @param onBackInvokedCallback   The back callback registered by the current top level window.
+     * @param type                  The {@link BackTargetType} of the destination (what will be
+     * @param onBackNavigationDone  The callback to be called once the client is done with the
+     *                              back preview.
+     * @param onBackInvokedCallback The back callback registered by the current top level window.
      */
     private BackNavigationInfo(@BackTargetType int type,
             @Nullable RemoteCallback onBackNavigationDone,
             @Nullable IOnBackInvokedCallback onBackInvokedCallback,
             boolean isPrepareRemoteAnimation,
+            boolean isAnimationCallback,
             @Nullable CustomAnimationInfo customAnimationInfo) {
         mType = type;
         mOnBackNavigationDone = onBackNavigationDone;
         mOnBackInvokedCallback = onBackInvokedCallback;
         mPrepareRemoteAnimation = isPrepareRemoteAnimation;
+        mAnimationCallback = isAnimationCallback;
         mCustomAnimationInfo = customAnimationInfo;
     }
 
@@ -122,6 +125,7 @@
         mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR);
         mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
         mPrepareRemoteAnimation = in.readBoolean();
+        mAnimationCallback = in.readBoolean();
         mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR);
     }
 
@@ -132,6 +136,7 @@
         dest.writeTypedObject(mOnBackNavigationDone, flags);
         dest.writeStrongInterface(mOnBackInvokedCallback);
         dest.writeBoolean(mPrepareRemoteAnimation);
+        dest.writeBoolean(mAnimationCallback);
         dest.writeTypedObject(mCustomAnimationInfo, flags);
     }
 
@@ -159,7 +164,7 @@
     }
 
     /**
-     * Return true if the core is preparing a back gesture nimation.
+     * Return true if the core is preparing a back gesture animation.
      * @hide
      */
     public boolean isPrepareRemoteAnimation() {
@@ -167,6 +172,14 @@
     }
 
     /**
+     * Return true if the callback is {@link OnBackAnimationCallback}.
+     * @hide
+     */
+    public boolean isAnimationCallback() {
+        return mAnimationCallback;
+    }
+
+    /**
      * Callback to be called when the back preview is finished in order to notify the server that
      * it can clean up the resources created for the animation.
      * @hide
@@ -214,6 +227,8 @@
                 + "mType=" + typeToString(mType) + " (" + mType + ")"
                 + ", mOnBackNavigationDone=" + mOnBackNavigationDone
                 + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
+                + ", mPrepareRemoteAnimation=" + mPrepareRemoteAnimation
+                + ", mAnimationCallback=" + mAnimationCallback
                 + ", mCustomizeAnimationInfo=" + mCustomAnimationInfo
                 + '}';
     }
@@ -343,6 +358,7 @@
         private IOnBackInvokedCallback mOnBackInvokedCallback = null;
         private boolean mPrepareRemoteAnimation;
         private CustomAnimationInfo mCustomAnimationInfo;
+        private boolean mAnimationCallback = false;
 
         /**
          * @see BackNavigationInfo#getType()
@@ -387,6 +403,7 @@
             mCustomAnimationInfo.mWindowAnimations = windowAnimations;
             return this;
         }
+
         /**
          * Set resources ids for customize activity animation.
          */
@@ -402,12 +419,21 @@
         }
 
         /**
+         * @param isAnimationCallback whether the callback is {@link OnBackAnimationCallback}
+         */
+        public Builder setAnimationCallback(boolean isAnimationCallback) {
+            mAnimationCallback = isAnimationCallback;
+            return this;
+        }
+
+        /**
          * Builds and returns an instance of {@link BackNavigationInfo}
          */
         public BackNavigationInfo build() {
             return new BackNavigationInfo(mType, mOnBackNavigationDone,
                     mOnBackInvokedCallback,
                     mPrepareRemoteAnimation,
+                    mAnimationCallback,
                     mCustomAnimationInfo);
         }
     }
diff --git a/core/java/android/window/OnBackInvokedCallbackInfo.java b/core/java/android/window/OnBackInvokedCallbackInfo.java
index 6480da3..bb5fe96 100644
--- a/core/java/android/window/OnBackInvokedCallbackInfo.java
+++ b/core/java/android/window/OnBackInvokedCallbackInfo.java
@@ -28,15 +28,20 @@
     @NonNull
     private final IOnBackInvokedCallback mCallback;
     private @OnBackInvokedDispatcher.Priority int mPriority;
+    private final boolean mIsAnimationCallback;
 
-    public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback, int priority) {
+    public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback,
+            int priority,
+            boolean isAnimationCallback) {
         mCallback = callback;
         mPriority = priority;
+        mIsAnimationCallback = isAnimationCallback;
     }
 
     private OnBackInvokedCallbackInfo(@NonNull Parcel in) {
         mCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
         mPriority = in.readInt();
+        mIsAnimationCallback = in.readBoolean();
     }
 
     @Override
@@ -48,6 +53,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongInterface(mCallback);
         dest.writeInt(mPriority);
+        dest.writeBoolean(mIsAnimationCallback);
     }
 
     public static final Creator<OnBackInvokedCallbackInfo> CREATOR =
@@ -77,9 +83,16 @@
         return mPriority;
     }
 
+    public boolean isAnimationCallback() {
+        return mIsAnimationCallback;
+    }
+
     @Override
     public String toString() {
         return "OnBackInvokedCallbackInfo{"
-                + "mCallback=" + mCallback + ", mPriority=" + mPriority + '}';
+                + "mCallback=" + mCallback
+                + ", mPriority=" + mPriority
+                + ", mIsAnimationCallback=" + mIsAnimationCallback
+                + '}';
     }
 }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 8066f50..51382a4 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -193,7 +193,10 @@
                                 ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
                                         callback).getIOnBackInvokedCallback()
                                 : new OnBackInvokedCallbackWrapper(callback);
-                callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
+                callbackInfo = new OnBackInvokedCallbackInfo(
+                        iCallback,
+                        priority,
+                        callback instanceof OnBackAnimationCallback);
             }
             mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
         } catch (RemoteException e) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 2b08a55..853fe2f 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -77,6 +77,10 @@
         /** Gating the removal of sorting-notifications-by-interruptiveness. */
         public static final Flag NO_SORT_BY_INTERRUPTIVENESS =
                 devFlag("persist.sysui.notification.no_sort_by_interruptiveness");
+
+        /** Gating the logging of DND state change events. */
+        public static final Flag LOG_DND_STATE_EVENTS =
+                devFlag("persist.sysui.notification.log_dnd_state_events");
     }
 
     //// == End of flags.  Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 1b4afd6..bb8bdf5 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -255,6 +255,12 @@
                 return "HIDE_SOFT_INPUT_IMM_DEPRECATION";
             case SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR:
                 return "HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR";
+            case SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS:
+                return "SHOW_IME_SCREENSHOT_FROM_IMMS";
+            case SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS:
+                return "REMOVE_IME_SCREENSHOT_FROM_IMMS";
+            case SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE:
+                return "HIDE_WHEN_INPUT_TARGET_INVISIBLE";
             default:
                 return "Unknown=" + reason;
         }
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index ec9184b..6e9cd44 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -65,7 +65,10 @@
         SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
         SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED,
         SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION,
-        SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR
+        SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR,
+        SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS,
+        SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS,
+        SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE,
 })
 public @interface SoftInputShowHideReason {
     /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
@@ -259,4 +262,20 @@
      */
     int HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR =
             ImeProtoEnums.REASON_HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR;
+
+    /**
+     * Shows ime screenshot by {@link com.android.server.inputmethod.InputMethodManagerService}.
+     */
+    int SHOW_IME_SCREENSHOT_FROM_IMMS = ImeProtoEnums.REASON_SHOW_IME_SCREENSHOT_FROM_IMMS;
+
+    /**
+     * Removes ime screenshot by {@link com.android.server.inputmethod.InputMethodManagerService}.
+     */
+    int REMOVE_IME_SCREENSHOT_FROM_IMMS = ImeProtoEnums.REASON_REMOVE_IME_SCREENSHOT_FROM_IMMS;
+
+    /**
+     * Hide soft input when the input target being removed or being obscured by an non-IME
+     * focusable overlay window.
+     */
+    int HIDE_WHEN_INPUT_TARGET_INVISIBLE = ImeProtoEnums.REASON_HIDE_WHEN_INPUT_TARGET_INVISIBLE;
 }
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/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/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index bb3089b..325ebbe 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -458,6 +458,7 @@
     optional float global_scale = 44;
     repeated .android.graphics.RectProto keep_clear_areas = 45;
     repeated .android.graphics.RectProto unrestricted_keep_clear_areas = 46;
+    repeated .android.view.InsetsSourceProto mergedLocalInsetsSources = 47;
 }
 
 message IdentifierProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 05b38a5..c928a82 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1036,9 +1036,9 @@
         android:protectionLevel="dangerous" />
 
     <!-- @SystemApi @hide Allows an application to communicate over satellite.
-         Only granted if the application is a system app. -->
+         Only granted if the application is a system app or privileged app. -->
     <permission android:name="android.permission.SATELLITE_COMMUNICATION"
-                android:protectionLevel="internal|role" />
+                android:protectionLevel="role|signature|privileged" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing external storage                             -->
@@ -1466,6 +1466,9 @@
 
     <!-- Allows an application to initiate a phone call without going through
         the Dialer user interface for the user to confirm the call.
+        <p>
+        <em>Note: An app holding this permission can also call carrier MMI codes to change settings
+        such as call forwarding or call waiting preferences.
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.CALL_PHONE"
@@ -2539,7 +2542,7 @@
     <permission android:name="android.permission.TURN_SCREEN_ON"
         android:label="@string/permlab_turnScreenOn"
         android:description="@string/permdesc_turnScreenOn"
-        android:protectionLevel="normal|appop" />
+        android:protectionLevel="signature|privileged|appop" />
 
     <!-- ==================================================== -->
     <!-- Permissions related to changing audio settings   -->
@@ -2917,6 +2920,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..a57a051 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" />
@@ -142,10 +146,6 @@
     <bool name="config_enhanced_iwlan_handover_check">true</bool>
     <java-symbol type="bool" name="config_enhanced_iwlan_handover_check" />
 
-    <!-- Whether using the new SubscriptionManagerService or the old SubscriptionController -->
-    <bool name="config_using_subscription_manager_service">true</bool>
-    <java-symbol type="bool" name="config_using_subscription_manager_service" />
-
     <!-- Whether asynchronously update the subscription database or not. Async mode increases
          the performance, but sync mode reduces the chance of database/cache out-of-sync. -->
     <bool name="config_subscription_database_async_update">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 947dc2d..c6462f1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1452,7 +1452,8 @@
       without your intervention. This may result in unexpected charges or calls.
       Note that this doesn\'t allow the app to call emergency numbers.
       Malicious apps may cost you money by making calls without your
-      confirmation.</string>
+      confirmation, or dial carrier codes which cause incoming calls to be
+      automatically forwarded to another number.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessImsCallService">access IMS call service</string>
@@ -1970,8 +1971,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] -->
@@ -5896,9 +5897,9 @@
     <string name="resolver_cant_access_personal_apps_explanation">This content can\u2019t be opened with personal apps</string>
 
     <!-- Error message. This text lets the user know that they need to turn on work apps in order to share or open content. There's also a button a user can tap to turn on the apps. [CHAR LIMIT=NONE] -->
-    <string name="resolver_turn_on_work_apps">Work profile is paused</string>
-    <!-- Button text. This button turns on a user's work profile so they can access their work apps and data. [CHAR LIMIT=NONE] -->
-    <string name="resolver_switch_on_work">Tap to turn on</string>
+    <string name="resolver_turn_on_work_apps">Work apps are paused</string>
+    <!-- Button text. This button unpauses a user's work apps and data. [CHAR LIMIT=NONE] -->
+    <string name="resolver_switch_on_work">Unpause</string>
 
     <!-- Error message. This text lets the user know that their current work apps don't support the specific content. [CHAR LIMIT=NONE] -->
     <string name="resolver_no_work_apps_available">No work apps</string>
@@ -5907,9 +5908,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/res/drawable-nodpi/test_too_big.png b/core/tests/coretests/res/drawable-nodpi/test_too_big.png
new file mode 100644
index 0000000..3754072
--- /dev/null
+++ b/core/tests/coretests/res/drawable-nodpi/test_too_big.png
Binary files differ
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index a53d57f..a102b3e 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -1127,6 +1127,31 @@
         mActivityRule.runOnUiThread(() -> {});
     }
 
+    @Test
+    public void restartValueAnimator() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ValueAnimator.AnimatorUpdateListener listener = new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                if (((float) animation.getAnimatedValue()) != A1_START_VALUE) {
+                    latch.countDown();
+                }
+            }
+        };
+        a1.addUpdateListener(listener);
+
+        mActivityRule.runOnUiThread(() -> {
+            a1.start();
+        });
+
+        // wait for a change in the value
+        assertTrue(latch.await(2, TimeUnit.SECONDS));
+
+        mActivityRule.runOnUiThread(() -> {
+            a1.start();
+            assertEquals(A1_START_VALUE, a1.getAnimatedValue());
+        });
+    }
     class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener {
         boolean wasRunning = false;
         long firstRunningFrameTime = -1;
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/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 75390a2..5d92296 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -20,6 +20,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -371,6 +372,90 @@
         }
     }
 
+    private int getMaxWidth(int origWidth, int origHeight, int maxNumPixels) {
+        float aspRatio = (float) origWidth / (float) origHeight;
+        int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio);
+        return (int) (newHeight * aspRatio);
+    }
+
+    private int getMaxHeight(int origWidth, int origHeight, int maxNumPixels) {
+        float aspRatio = (float) origWidth / (float) origHeight;
+        return (int) Math.sqrt(maxNumPixels / aspRatio);
+    }
+
+    @SmallTest
+    public void testScaleDownMaxSizeWithBitmap() throws Exception {
+        final int bmpWidth = 13_000;
+        final int bmpHeight = 10_000;
+        final int bmpBpp = 4;
+        final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp;
+        final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels);
+        final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels);
+
+        final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
+        final Icon ic = Icon.createWithBitmap(bm);
+        final Drawable drawable = ic.loadDrawable(mContext);
+
+        assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth);
+        assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
+    }
+
+    @SmallTest
+    public void testScaleDownMaxSizeWithAdaptiveBitmap() throws Exception {
+        final int bmpWidth = 20_000;
+        final int bmpHeight = 10_000;
+        final int bmpBpp = 4;
+        final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp;
+        final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels);
+        final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels);
+
+        final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
+        final Icon ic = Icon.createWithAdaptiveBitmap(bm);
+        final AdaptiveIconDrawable adaptiveDrawable = (AdaptiveIconDrawable) ic.loadDrawable(
+                mContext);
+        final Drawable drawable = adaptiveDrawable.getForeground();
+
+        assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth);
+        assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
+    }
+
+    @SmallTest
+    public void testScaleDownMaxSizeWithResource() throws Exception {
+        final Icon ic = Icon.createWithResource(getContext(), R.drawable.test_too_big);
+        final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
+
+        assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
+    }
+
+    @SmallTest
+    public void testScaleDownMaxSizeWithFile() throws Exception {
+        final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.test_too_big))
+                .getBitmap();
+        final File dir = getContext().getExternalFilesDir(null);
+        final File file1 = new File(dir, "file1-too-big.png");
+        bit1.compress(Bitmap.CompressFormat.PNG, 100,
+                new FileOutputStream(file1));
+
+        final Icon ic = Icon.createWithFilePath(file1.toString());
+        final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
+
+        assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
+    }
+
+    @SmallTest
+    public void testScaleDownMaxSizeWithData() throws Exception {
+        final int bmpBpp = 4;
+        final Bitmap originalBits = ((BitmapDrawable) getContext().getDrawable(
+                R.drawable.test_too_big)).getBitmap();
+        final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
+                originalBits.getWidth() * originalBits.getHeight() * bmpBpp);
+        originalBits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
+        final byte[] pngdata = ostream.toByteArray();
+        final Icon ic = Icon.createWithData(pngdata, 0, pngdata.length);
+        final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
+
+        assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
+    }
 
     // ======== utils ========
 
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/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..40cb7f2 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,10 @@
         <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"/>
+        <!-- Permission required for CTS test - SatelliteManagerTest -->
+        <permission name="android.permission.SATELLITE_COMMUNICATION"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index a76d74e..708feeb 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -35,6 +35,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BlendMode;
 import android.graphics.PorterDuff;
+import android.graphics.RecordingCanvas;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -70,6 +71,7 @@
 
 public final class Icon implements Parcelable {
     private static final String TAG = "Icon";
+    private static final boolean DEBUG = false;
 
     /**
      * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}.
@@ -361,15 +363,52 @@
     }
 
     /**
+     * Resizes image if size too large for Canvas to draw
+     * @param bitmap Bitmap to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE}
+     * @return resized bitmap
+     */
+    private Bitmap fixMaxBitmapSize(Bitmap bitmap) {
+        if (bitmap != null && bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
+            int bytesPerPixel = bitmap.getRowBytes() / bitmap.getWidth();
+            int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bytesPerPixel;
+            float aspRatio = (float) bitmap.getWidth() / (float) bitmap.getHeight();
+            int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio);
+            int newWidth = (int) (newHeight * aspRatio);
+
+            if (DEBUG) {
+                Log.d(TAG,
+                        "Image size too large: " + bitmap.getByteCount() + ". Resizing bitmap to: "
+                                + newWidth + " " + newHeight);
+            }
+
+            return scaleDownIfNecessary(bitmap, newWidth, newHeight);
+        }
+        return bitmap;
+    }
+
+    /**
+     * Resizes BitmapDrawable if size too large for Canvas to draw
+     * @param drawable Drawable to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE}
+     * @return resized Drawable
+     */
+    private Drawable fixMaxBitmapSize(Resources res, Drawable drawable) {
+        if (drawable instanceof BitmapDrawable) {
+            Bitmap scaledBmp = fixMaxBitmapSize(((BitmapDrawable) drawable).getBitmap());
+            return new BitmapDrawable(res, scaledBmp);
+        }
+        return drawable;
+    }
+
+    /**
      * Do the heavy lifting of loading the drawable, but stop short of applying any tint.
      */
     private Drawable loadDrawableInner(Context context) {
         switch (mType) {
             case TYPE_BITMAP:
-                return new BitmapDrawable(context.getResources(), getBitmap());
+                return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap()));
             case TYPE_ADAPTIVE_BITMAP:
                 return new AdaptiveIconDrawable(null,
-                    new BitmapDrawable(context.getResources(), getBitmap()));
+                    new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap())));
             case TYPE_RESOURCE:
                 if (getResources() == null) {
                     // figure out where to load resources from
@@ -400,7 +439,8 @@
                     }
                 }
                 try {
-                    return getResources().getDrawable(getResId(), context.getTheme());
+                    return fixMaxBitmapSize(getResources(),
+                            getResources().getDrawable(getResId(), context.getTheme()));
                 } catch (RuntimeException e) {
                     Log.e(TAG, String.format("Unable to load resource 0x%08x from pkg=%s",
                                     getResId(),
@@ -409,21 +449,21 @@
                 }
                 break;
             case TYPE_DATA:
-                return new BitmapDrawable(context.getResources(),
-                    BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength())
-                );
+                return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(
+                        BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(),
+                                getDataLength())));
             case TYPE_URI:
                 InputStream is = getUriInputStream(context);
                 if (is != null) {
                     return new BitmapDrawable(context.getResources(),
-                            BitmapFactory.decodeStream(is));
+                            fixMaxBitmapSize(BitmapFactory.decodeStream(is)));
                 }
                 break;
             case TYPE_URI_ADAPTIVE_BITMAP:
                 is = getUriInputStream(context);
                 if (is != null) {
                     return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(),
-                            BitmapFactory.decodeStream(is)));
+                            fixMaxBitmapSize(BitmapFactory.decodeStream(is))));
                 }
                 break;
         }
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/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index dcce4698..ab64f9e 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -67,7 +67,7 @@
 
     <!-- Temporarily extending the background to show an edu text hint for opening the menu -->
     <FrameLayout
-        android:id="@+id/tv_pip_menu_edu_text_drawer_placeholder"
+        android:id="@+id/tv_pip_menu_edu_text_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_below="@+id/tv_pip"
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 684a23a..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
@@ -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..b6216b3 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() {
@@ -619,11 +623,11 @@
      * Removes PiP immediately.
      */
     public void removePip() {
-        if (!mPipTransitionState.isInPip() || mToken == null) {
+        if (!mPipTransitionState.isInPip() || mToken == null || mLeash == null) {
             ProtoLog.wtf(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                     "%s: Not allowed to removePip in current state"
-                            + " mState=%d mToken=%s", TAG, mPipTransitionState.getTransitionState(),
-                    mToken);
+                            + " mState=%d mToken=%s mLeash=%s", TAG,
+                    mPipTransitionState.getTransitionState(), mToken, mLeash);
             return;
         }
 
@@ -1007,6 +1011,7 @@
             return;
         }
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mPipTransitionController.onFixedRotationFinished();
             clearWaitForFixedRotation();
             return;
         }
@@ -1527,6 +1532,9 @@
             if (snapshotSurface != null) {
                 mSyncTransactionQueue.queue(wct);
                 mSyncTransactionQueue.runInSync(t -> {
+                    // reset the pinch gesture
+                    maybePerformFinishResizeCallback();
+
                     // Scale the snapshot from its pre-resize bounds to the post-resize bounds.
                     mSurfaceTransactionHelper.scale(t, snapshotSurface, preResizeBounds,
                             snapshotDest);
@@ -1606,6 +1614,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/pip/tv/TvPipAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
index 222307f..5f6b3fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
@@ -31,6 +31,12 @@
 
 abstract class TvPipAction {
 
+    /**
+     * Extras key for adding a boolean to the {@link Notification.Action} to differentiate custom
+     * from system actions, most importantly to identify custom close actions.
+     **/
+    public static final String EXTRA_IS_PIP_CUSTOM_ACTION = "EXTRA_IS_PIP_CUSTOM_ACTION";
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"ACTION_"}, value = {
             ACTION_FULLSCREEN,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
index bca27a5..977aad4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
@@ -86,7 +86,7 @@
         Bundle extras = new Bundle();
         extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
                 mRemoteAction.getContentDescription());
-        extras.putBoolean(Notification.EXTRA_CONTAINS_CUSTOM_VIEW, true);
+        extras.putBoolean(TvPipAction.EXTRA_IS_PIP_CUSTOM_ACTION, true);
         builder.addExtras(extras);
 
         builder.setSemanticAction(isCloseAction()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
index 6eef225..f86f987 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
@@ -23,6 +23,7 @@
 
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;
 
+import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -115,6 +116,10 @@
         scheduleLifecycleEvents();
     }
 
+    int getEduTextDrawerHeight() {
+        return getVisibility() == GONE ? 0 : getHeight();
+    }
+
     private void scheduleLifecycleEvents() {
         final int startScrollDelay = mContext.getResources().getInteger(
                 R.integer.pip_edu_text_start_scroll_delay);
@@ -226,20 +231,41 @@
                 .start();
 
         // Start animation to close the drawer by animating its height to 0
-        final ValueAnimator heightAnimation = ValueAnimator.ofInt(getHeight(), 0);
-        heightAnimation.setDuration(eduTextSlideExitAnimationDuration);
-        heightAnimation.setInterpolator(TvPipInterpolators.BROWSE);
-        heightAnimation.addUpdateListener(animator -> {
+        final ValueAnimator heightAnimator = ValueAnimator.ofInt(getHeight(), 0);
+        heightAnimator.setDuration(eduTextSlideExitAnimationDuration);
+        heightAnimator.setInterpolator(TvPipInterpolators.BROWSE);
+        heightAnimator.addUpdateListener(animator -> {
             final ViewGroup.LayoutParams params = getLayoutParams();
             params.height = (int) animator.getAnimatedValue();
             setLayoutParams(params);
-            if (params.height == 0) {
-                setVisibility(GONE);
+        });
+        heightAnimator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(@NonNull Animator animator) {
+            }
+
+            @Override
+            public void onAnimationEnd(@NonNull Animator animator) {
+                onCloseEduTextAnimationEnd();
+            }
+
+            @Override
+            public void onAnimationCancel(@NonNull Animator animator) {
+                onCloseEduTextAnimationEnd();
+            }
+
+            @Override
+            public void onAnimationRepeat(@NonNull Animator animator) {
             }
         });
-        heightAnimation.start();
+        heightAnimator.start();
 
-        mListener.onCloseEduText();
+        mListener.onCloseEduTextAnimationStart();
+    }
+
+    public void onCloseEduTextAnimationEnd() {
+        setVisibility(GONE);
+        mListener.onCloseEduTextAnimationEnd();
     }
 
     /**
@@ -270,11 +296,8 @@
      * A listener for edu text drawer event states.
      */
     interface Listener {
-        /**
-         *  The edu text closing impacts the size of the Picture-in-Picture window and influences
-         *  how it is positioned on the screen.
-         */
-        void onCloseEduText();
+        void onCloseEduTextAnimationStart();
+        void onCloseEduTextAnimationEnd();
     }
 
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 6eb719b..d076418 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -57,7 +57,8 @@
  * A View that represents Pip Menu on TV. It's responsible for displaying the Pip menu actions from
  * the TvPipActionsProvider as well as the buttons for manually moving the PiP.
  */
-public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.Listener {
+public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.Listener,
+        TvPipMenuEduTextDrawer.Listener {
     private static final String TAG = "TvPipMenuView";
 
     private final TvPipMenuView.Listener mListener;
@@ -76,6 +77,7 @@
     private final View mDimLayer;
 
     private final TvPipMenuEduTextDrawer mEduTextDrawer;
+    private final ViewGroup mEduTextContainer;
 
     private final int mPipMenuOuterSpace;
     private final int mPipMenuBorderWidth;
@@ -139,9 +141,9 @@
         mPipMenuBorderWidth = context.getResources()
                 .getDimensionPixelSize(R.dimen.pip_menu_border_width);
 
-        mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, mListener);
-        ((FrameLayout) findViewById(R.id.tv_pip_menu_edu_text_drawer_placeholder))
-                .addView(mEduTextDrawer);
+        mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, this);
+        mEduTextContainer = (ViewGroup) findViewById(R.id.tv_pip_menu_edu_text_container);
+        mEduTextContainer.addView(mEduTextDrawer);
     }
 
     void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
@@ -235,11 +237,13 @@
      * pip menu when it gains focus.
      */
     private void updatePipFrameBounds() {
-        final ViewGroup.LayoutParams pipFrameParams = mPipFrameView.getLayoutParams();
-        if (pipFrameParams != null) {
-            pipFrameParams.width = mCurrentPipBounds.width() + 2 * mPipMenuBorderWidth;
-            pipFrameParams.height = mCurrentPipBounds.height() + 2 * mPipMenuBorderWidth;
-            mPipFrameView.setLayoutParams(pipFrameParams);
+        if (mPipFrameView.getVisibility() == VISIBLE) {
+            final ViewGroup.LayoutParams pipFrameParams = mPipFrameView.getLayoutParams();
+            if (pipFrameParams != null) {
+                pipFrameParams.width = mCurrentPipBounds.width() + 2 * mPipMenuBorderWidth;
+                pipFrameParams.height = mCurrentPipBounds.height() + 2 * mPipMenuBorderWidth;
+                mPipFrameView.setLayoutParams(pipFrameParams);
+            }
         }
 
         final ViewGroup.LayoutParams pipViewParams = mPipView.getLayoutParams();
@@ -262,7 +266,7 @@
     Rect getPipMenuContainerBounds(Rect pipBounds) {
         final Rect menuUiBounds = new Rect(pipBounds);
         menuUiBounds.inset(-mPipMenuOuterSpace, -mPipMenuOuterSpace);
-        menuUiBounds.bottom += mEduTextDrawer.getHeight();
+        menuUiBounds.bottom += mEduTextDrawer.getEduTextDrawerHeight();
         return menuUiBounds;
     }
 
@@ -406,6 +410,17 @@
     }
 
     @Override
+    public void onCloseEduTextAnimationStart() {
+        mListener.onCloseEduText();
+    }
+
+    @Override
+    public void onCloseEduTextAnimationEnd() {
+        mPipFrameView.setVisibility(GONE);
+        mEduTextContainer.setVisibility(GONE);
+    }
+
+    @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         if (event.getAction() == ACTION_UP) {
 
@@ -551,7 +566,7 @@
         }
     }
 
-    interface Listener extends TvPipMenuEduTextDrawer.Listener {
+    interface Listener {
 
         void onBackPress();
 
@@ -573,5 +588,11 @@
          * has lost focus.
          */
         void onPipWindowFocusChanged(boolean focused);
+
+        /**
+         *  The edu text closing impacts the size of the Picture-in-Picture window and influences
+         *  how it is positioned on the screen.
+         */
+        void onCloseEduText();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index c9b3a1a..c4a0e9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -32,6 +32,8 @@
             Consts.TAG_WM_SHELL),
     WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM_SHELL),
+    WM_SHELL_RECENTS_TRANSITION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            Consts.TAG_WM_SHELL),
     WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM_SHELL),
     WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
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 44d7e6d..b554872 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
@@ -47,7 +47,9 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.util.TransitionUtil;
@@ -96,6 +98,8 @@
 
     void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
             IApplicationThread appThread, IRecentsAnimationRunner listener) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                "RecentsTransitionHandler.startRecentsTransition");
         // only care about latest one.
         mAnimApp = appThread;
         WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -116,7 +120,7 @@
             mixer.setRecentsTransition(transition);
         }
         if (transition == null) {
-            controller.cancel();
+            controller.cancel("startRecentsTransition");
             return;
         }
         controller.setTransition(transition);
@@ -127,6 +131,7 @@
     public WindowContainerTransaction handleRequest(IBinder transition,
             TransitionRequestInfo request) {
         // do not directly handle requests. Only entry point should be via startRecentsTransition
+        Slog.e(TAG, "RecentsTransitionHandler.handleRequest: Unexpected transition request");
         return null;
     }
 
@@ -143,11 +148,17 @@
             SurfaceControl.Transaction finishTransaction,
             Transitions.TransitionFinishCallback finishCallback) {
         final int controllerIdx = findController(transition);
-        if (controllerIdx < 0) return false;
+        if (controllerIdx < 0) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsTransitionHandler.startAnimation: no controller found");
+            return false;
+        }
         final RecentsController controller = mControllers.get(controllerIdx);
         Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
         mAnimApp = null;
         if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsTransitionHandler.startAnimation: failed to start animation");
             return false;
         }
         return true;
@@ -168,7 +179,7 @@
             SurfaceControl.Transaction finishTransaction) {
         final int idx = findController(transition);
         if (idx < 0) return;
-        mControllers.get(idx).cancel();
+        mControllers.get(idx).cancel("onTransitionConsumed");
     }
 
     /** There is only one of these and it gets reset on finish. */
@@ -213,39 +224,45 @@
 
         RecentsController(IRecentsAnimationRunner listener) {
             mListener = listener;
-            mDeathHandler = () -> mExecutor.execute(() -> {
-                if (mListener == null) return;
-                if (mFinishCB != null) {
-                    finish(mWillFinishToHome, false /* leaveHint */);
-                }
-            });
+            mDeathHandler = () -> {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.DeathRecipient: binder died");
+                finish(mWillFinishToHome, false /* leaveHint */);
+            };
             try {
                 mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
             } catch (RemoteException e) {
+                Slog.e(TAG, "RecentsController: failed to link to death", e);
                 mListener = null;
             }
         }
 
         void setTransition(IBinder transition) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsController.setTransition: id=%s", transition);
             mTransition = transition;
         }
 
-        void cancel() {
+        void cancel(String reason) {
             // restoring (to-home = false) involves submitting more WM changes, so by default, use
             // toHome = true when canceling.
-            cancel(true /* toHome */);
+            cancel(true /* toHome */, reason);
         }
 
-        void cancel(boolean toHome) {
+        void cancel(boolean toHome, String reason) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsController.cancel: toHome=%b reason=%s", toHome, reason);
             if (mListener != null) {
                 try {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                            "RecentsController.cancel: calling onAnimationCanceled");
                     mListener.onAnimationCanceled(null, null);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Error canceling recents animation", e);
                 }
             }
             if (mFinishCB != null) {
-                finish(toHome, false /* userLeave */);
+                finishInner(toHome, false /* userLeave */);
             } else {
                 cleanUp();
             }
@@ -272,6 +289,8 @@
                 }
             }
             try {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.cancel: calling onAnimationCanceled with snapshots");
                 mListener.onAnimationCanceled(taskIds, snapshots);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error canceling recents animation", e);
@@ -281,6 +300,7 @@
         }
 
         void cleanUp() {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.cleanup");
             if (mListener != null && mDeathHandler != null) {
                 mListener.asBinder().unlinkToDeath(mDeathHandler, 0 /* flags */);
                 mDeathHandler = null;
@@ -304,6 +324,7 @@
 
         boolean start(TransitionInfo info, SurfaceControl.Transaction t,
                 SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCB) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.start");
             if (mListener == null || mTransition == null) {
                 cleanUp();
                 return false;
@@ -363,6 +384,8 @@
                             info.getChanges().size() - i, info, t, mLeashMap);
                     apps.add(target);
                     if (TransitionUtil.isClosingType(change.getMode())) {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "  adding pausing taskId=%d", taskInfo.taskId);
                         // raise closing (pausing) task to "above" layer so it isn't covered
                         t.setLayer(target.leash, info.getChanges().size() * 3 - i);
                         mPausingTasks.add(new TaskState(change, target.leash));
@@ -377,19 +400,23 @@
                     } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
                         // do nothing
                     } else if (TransitionUtil.isOpeningType(change.getMode())) {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "  adding opening taskId=%d", taskInfo.taskId);
                         mOpeningTasks.add(new TaskState(change, target.leash));
                     }
                 }
             }
             t.apply();
             try {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.start: calling onAnimationStart");
                 mListener.onAnimationStart(this,
                         apps.toArray(new RemoteAnimationTarget[apps.size()]),
                         wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
                         new Rect(0, 0, 0, 0), new Rect());
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error starting recents animation", e);
-                cancel();
+                cancel("onAnimationStart() failed");
             }
             return true;
         }
@@ -398,14 +425,19 @@
         void merge(TransitionInfo info, SurfaceControl.Transaction t,
                 Transitions.TransitionFinishCallback finishCallback) {
             if (mFinishCB == null) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.merge: skip, no finish callback");
                 // This was no-op'd (likely a repeated start) and we've already sent finish.
                 return;
             }
             if (info.getType() == TRANSIT_SLEEP) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.merge: transit_sleep");
                 // A sleep event means we need to stop animations immediately, so cancel here.
-                cancel();
+                cancel("transit_sleep");
                 return;
             }
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.merge");
             ArrayList<TransitionInfo.Change> openingTasks = null;
             ArrayList<TransitionInfo.Change> closingTasks = null;
             mOpeningSeparateHome = false;
@@ -422,7 +454,7 @@
                         && taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
                     // Tasks that are always on top (e.g. bubbles), will handle their own transition
                     // as they are on top of everything else. So cancel the merge here.
-                    cancel();
+                    cancel("task #" + taskInfo.taskId + " is always_on_top");
                     return;
                 }
                 hasTaskChange = hasTaskChange || taskInfo != null;
@@ -453,7 +485,7 @@
                     // Finish recents animation if the display is changed, so the default
                     // transition handler can play the animation such as rotation effect.
                     if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) {
-                        cancel(mWillFinishToHome);
+                        cancel(mWillFinishToHome, "display change");
                         return;
                     }
                     // Don't consider order-only changes as changing apps.
@@ -497,7 +529,10 @@
                                 + " something unexpected: " + change.getTaskInfo().taskId);
                         continue;
                     }
-                    mPausingTasks.add(mOpeningTasks.remove(openingIdx));
+                    final TaskState openingTask = mOpeningTasks.remove(openingIdx);
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                            "  pausing opening taskId=%d", openingTask.mTaskInfo.taskId);
+                    mPausingTasks.add(openingTask);
                     didMergeThings = true;
                 }
             }
@@ -514,7 +549,10 @@
                         // Something is showing/opening a previously-pausing app.
                         appearedTargets[i] = TransitionUtil.newTarget(
                                 change, layer, mPausingTasks.get(pausingIdx).mLeash);
-                        mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
+                        final TaskState pausingTask = mPausingTasks.remove(pausingIdx);
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "  opening pausing taskId=%d", pausingTask.mTaskInfo.taskId);
+                        mOpeningTasks.add(pausingTask);
                         // Setup hides opening tasks initially, so make it visible again (since we
                         // are already showing it).
                         t.show(change.getLeash());
@@ -527,6 +565,8 @@
                         final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
                         t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash());
                         t.setLayer(appearedTargets[i].leash, layer);
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                                "  opening new taskId=%d", appearedTargets[i].taskId);
                         mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash));
                     }
                 }
@@ -544,7 +584,7 @@
                         + foundRecentsClosing);
                 if (foundRecentsClosing) {
                     mWillFinishToHome = false;
-                    cancel(false /* toHome */);
+                    cancel(false /* toHome */, "didn't merge");
                 }
                 return;
             }
@@ -552,13 +592,15 @@
             t.apply();
             // not using the incoming anim-only surfaces
             info.releaseAnimSurfaces();
-            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
             if (appearedTargets == null) return;
             try {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.merge: calling onTasksAppeared");
                 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. */
@@ -577,6 +619,8 @@
         @Override
         public TaskSnapshot screenshotTask(int taskId) {
             try {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                        "RecentsController.screenshotTask: taskId=%d", taskId);
                 return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to screenshot task", e);
@@ -587,12 +631,19 @@
         @Override
         public void setInputConsumerEnabled(boolean enabled) {
             mExecutor.execute(() -> {
-                if (mFinishCB == null || !enabled) return;
+                if (mFinishCB == null || !enabled) {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                            "RecentsController.setInputConsumerEnabled: skip, cb?=%b enabled?=%b",
+                            mFinishCB != null, enabled);
+                    return;
+                }
                 // transient launches don't receive focus automatically. Since we are taking over
                 // the gesture now, take focus explicitly.
                 // This also moves recents back to top if the user gestured before a switch
                 // animation finished.
                 try {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                            "RecentsController.setInputConsumerEnabled: set focus to recents");
                     ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to set focused task", e);
@@ -607,6 +658,8 @@
         @Override
         public void setFinishTaskTransaction(int taskId,
                 PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsController.setFinishTaskTransaction: taskId=%d", taskId);
             mExecutor.execute(() -> {
                 if (mFinishCB == null) return;
                 mPipTransaction = finishTransaction;
@@ -624,6 +677,9 @@
                 Slog.e(TAG, "Duplicate call to finish");
                 return;
             }
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsController.finishInner: toHome=%b userLeaveHint=%b willFinishToHome=%b",
+                    toHome, sendUserLeaveHint, mWillFinishToHome);
             final Transitions.TransitionFinishCallback finishCB = mFinishCB;
             mFinishCB = null;
 
@@ -635,6 +691,7 @@
                 else wct.restoreTransientOrder(mRecentsTask);
             }
             if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "  returning to app");
                 // The gesture is returning to the pausing-task(s) rather than continuing with
                 // recents, so end the transition by moving the app back to the top (and also
                 // re-showing it's task).
@@ -647,6 +704,7 @@
                     wct.restoreTransientOrder(mRecentsTask);
                 }
             } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "  3p launching home");
                 // Special situation where 3p launcher was changed during recents (this happens
                 // during tapltests...). Here we get both "return to home" AND "home opening".
                 // This is basically going home, but we have to restore the recents and home order.
@@ -665,6 +723,7 @@
                     wct.restoreTransientOrder(mRecentsTask);
                 }
             } else {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "  normal finish");
                 // The general case: committing to recents, going home, or switching tasks.
                 for (int i = 0; i < mOpeningTasks.size(); ++i) {
                     t.show(mOpeningTasks.get(i).mTaskSurface);
@@ -721,6 +780,8 @@
          */
         @Override
         public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+                    "RecentsController.detachNavigationBarFromApp");
             mExecutor.execute(() -> {
                 if (mTransition == null) return;
                 try {
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/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/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
index 1786d13..91c0a5b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
@@ -19,17 +19,11 @@
     val maxHeight: Float = 0f,
     val pixelDensity: Float = 1f,
     var color: Int = Color.WHITE,
-    val opacity: Int = RIPPLE_DEFAULT_ALPHA,
-    val sparkleStrength: Float = RIPPLE_SPARKLE_STRENGTH,
+    val opacity: Int = RippleShader.RIPPLE_DEFAULT_ALPHA,
+    val sparkleStrength: Float = RippleShader.RIPPLE_SPARKLE_STRENGTH,
     // Null means it uses default fade parameter values.
     val baseRingFadeParams: RippleShader.FadeParams? = null,
     val sparkleRingFadeParams: RippleShader.FadeParams? = null,
     val centerFillFadeParams: RippleShader.FadeParams? = null,
     val shouldDistort: Boolean = true
-) {
-    companion object {
-        const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
-        const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
-        const val RIPPLE_DEFAULT_ALPHA: Int = 115 // full opacity is 255.
-    }
-}
+)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
index b5b6037..7e56f4b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
@@ -60,6 +60,10 @@
         const val DEFAULT_CENTER_FILL_FADE_OUT_START = 0f
         const val DEFAULT_CENTER_FILL_FADE_OUT_END = 0.6f
 
+        const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+        const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
+        const val RIPPLE_DEFAULT_ALPHA: Int = 115 // full opacity is 255.
+
         private const val SHADER_UNIFORMS =
             """
             uniform vec2 in_center;
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index ef5ad43..b899127 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
@@ -72,9 +72,9 @@
         this.rippleShape = rippleShape
         rippleShader = RippleShader(rippleShape)
 
-        rippleShader.color = RippleAnimationConfig.RIPPLE_DEFAULT_COLOR
+        rippleShader.color = RippleShader.RIPPLE_DEFAULT_COLOR
         rippleShader.rawProgress = 0f
-        rippleShader.sparkleStrength = RippleAnimationConfig.RIPPLE_SPARKLE_STRENGTH
+        rippleShader.sparkleStrength = RippleShader.RIPPLE_SPARKLE_STRENGTH
         rippleShader.pixelDensity = resources.displayMetrics.density
 
         ripplePaint.shader = rippleShader
@@ -209,7 +209,7 @@
      *
      * The alpha value of the color will be applied to the ripple. The alpha range is [0-255].
      */
-    fun setColor(color: Int, alpha: Int = RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA) {
+    fun setColor(color: Int, alpha: Int = RippleShader.RIPPLE_DEFAULT_ALPHA) {
         rippleShader.color = ColorUtils.setAlphaComponent(color, alpha)
     }
 
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/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index 5b0e290..461d390 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -41,10 +41,10 @@
     var listeningForFaceAssistant: Boolean = false,
     var occludingAppRequestingFaceAuth: Boolean = false,
     var postureAllowsListening: Boolean = false,
-    var primaryUser: Boolean = false,
     var secureCameraLaunched: Boolean = false,
     var supportsDetect: Boolean = false,
     var switchingUser: Boolean = false,
+    var systemUser: Boolean = false,
     var udfpsFingerDown: Boolean = false,
     var userNotTrustedOrDetectionIsNeeded: Boolean = false,
 ) : KeyguardListenModel() {
@@ -69,11 +69,11 @@
             keyguardGoingAway.toString(),
             listeningForFaceAssistant.toString(),
             occludingAppRequestingFaceAuth.toString(),
-            primaryUser.toString(),
             postureAllowsListening.toString(),
             secureCameraLaunched.toString(),
             supportsDetect.toString(),
             switchingUser.toString(),
+            systemUser.toString(),
             alternateBouncerShowing.toString(),
             udfpsFingerDown.toString(),
             userNotTrustedOrDetectionIsNeeded.toString(),
@@ -109,12 +109,11 @@
                 keyguardGoingAway = model.keyguardGoingAway
                 listeningForFaceAssistant = model.listeningForFaceAssistant
                 occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth
-                primaryUser = model.primaryUser
                 postureAllowsListening = model.postureAllowsListening
                 secureCameraLaunched = model.secureCameraLaunched
                 supportsDetect = model.supportsDetect
                 switchingUser = model.switchingUser
-                switchingUser = model.switchingUser
+                systemUser = model.systemUser
                 udfpsFingerDown = model.udfpsFingerDown
                 userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded
             }
@@ -153,11 +152,11 @@
                 "keyguardGoingAway",
                 "listeningForFaceAssistant",
                 "occludingAppRequestingFaceAuth",
-                "primaryUser",
                 "postureAllowsListening",
                 "secureCameraLaunched",
                 "supportsDetect",
                 "switchingUser",
+                "systemUser",
                 "udfpsBouncerShowing",
                 "udfpsFingerDown",
                 "userNotTrustedOrDetectionIsNeeded",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index b8c0ccb..f2685c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -41,11 +41,11 @@
     var keyguardIsVisible: Boolean = false,
     var keyguardOccluded: Boolean = false,
     var occludingAppRequestingFp: Boolean = false,
-    var primaryUser: Boolean = false,
     var shouldListenSfpsState: Boolean = false,
     var shouldListenForFingerprintAssistant: Boolean = false,
     var strongerAuthRequired: Boolean = false,
     var switchingUser: Boolean = false,
+    var systemUser: Boolean = false,
     var udfps: Boolean = false,
     var userDoesNotHaveTrust: Boolean = false,
 ) : KeyguardListenModel() {
@@ -72,11 +72,11 @@
             keyguardIsVisible.toString(),
             keyguardOccluded.toString(),
             occludingAppRequestingFp.toString(),
-            primaryUser.toString(),
             shouldListenSfpsState.toString(),
             shouldListenForFingerprintAssistant.toString(),
             strongerAuthRequired.toString(),
             switchingUser.toString(),
+            systemUser.toString(),
             udfps.toString(),
             userDoesNotHaveTrust.toString(),
         )
@@ -112,11 +112,11 @@
                 keyguardIsVisible = model.keyguardIsVisible
                 keyguardOccluded = model.keyguardOccluded
                 occludingAppRequestingFp = model.occludingAppRequestingFp
-                primaryUser = model.primaryUser
                 shouldListenSfpsState = model.shouldListenSfpsState
                 shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant
                 strongerAuthRequired = model.strongerAuthRequired
                 switchingUser = model.switchingUser
+                systemUser = model.systemUser
                 udfps = model.udfps
                 userDoesNotHaveTrust = model.userDoesNotHaveTrust
             }
@@ -158,11 +158,11 @@
                 "keyguardIsVisible",
                 "keyguardOccluded",
                 "occludingAppRequestingFp",
-                "primaryUser",
                 "shouldListenSidFingerprintState",
                 "shouldListenForFingerprintAssistant",
                 "strongAuthRequired",
                 "switchingUser",
+                "systemUser",
                 "underDisplayFingerprint",
                 "userDoesNotHaveTrust",
             )
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 76e051e..693268d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -178,6 +178,7 @@
 
         @Override
         public void onUserInput() {
+            mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
             mUpdateMonitor.cancelFaceAuth();
         }
 
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..10c08bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -297,7 +297,7 @@
     private final Context mContext;
     private final UserTracker mUserTracker;
     private final KeyguardUpdateMonitorLogger mLogger;
-    private final boolean mIsPrimaryUser;
+    private final boolean mIsSystemUser;
     private final AuthController mAuthController;
     private final UiEventLogger mUiEventLogger;
     private final Set<Integer> mFaceAcquiredInfoIgnoreList;
@@ -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);
@@ -2526,7 +2522,7 @@
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
 
         TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
-        mIsPrimaryUser = mUserManager.isPrimaryUser();
+        mIsSystemUser = mUserManager.isSystemUser();
         int user = mUserTracker.getUserId();
         mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
         mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
@@ -2972,7 +2968,7 @@
                         || (mKeyguardOccluded && userDoesNotHaveTrust && mKeyguardShowing
                             && (mOccludingAppRequestingFp || isUdfps || mAlternateBouncerShowing));
 
-        // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
+        // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
         final boolean userCanSkipBouncer = getUserCanSkipBouncer(user);
@@ -2981,7 +2977,7 @@
                 !mSwitchingUser
                         && !fingerprintDisabledForUser
                         && (!mKeyguardGoingAway || !mDeviceInteractive)
-                        && mIsPrimaryUser
+                        && mIsSystemUser
                         && biometricEnabledForUser
                         && !isUserInLockdown(user);
         final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
@@ -3025,11 +3021,11 @@
                     isKeyguardVisible(),
                     mKeyguardOccluded,
                     mOccludingAppRequestingFp,
-                    mIsPrimaryUser,
                     shouldListenSideFpsState,
                     shouldListenForFingerprintAssistant,
                     strongerAuthRequired,
                     mSwitchingUser,
+                    mIsSystemUser,
                     isUdfps,
                     userDoesNotHaveTrust));
 
@@ -3074,7 +3070,7 @@
         final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
         final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown();
         final boolean isPostureAllowedForFaceAuth = doesPostureAllowFaceAuth(mPostureState);
-        // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
+        // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean shouldListen =
                 (mPrimaryBouncerFullyShown
@@ -3086,7 +3082,7 @@
                         || mAlternateBouncerShowing)
                 && !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
                 && !mKeyguardGoingAway && biometricEnabledForUser
-                && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
+                && faceAuthAllowedOrDetectionIsNeeded && mIsSystemUser
                 && (!mSecureCameraLaunched || mAlternateBouncerShowing)
                 && faceAndFpNotAuthenticated
                 && !mGoingToSleep
@@ -3112,10 +3108,10 @@
                     shouldListenForFaceAssistant,
                     mOccludingAppRequestingFace,
                     isPostureAllowedForFaceAuth,
-                    mIsPrimaryUser,
                     mSecureCameraLaunched,
                     supportsDetect,
                     mSwitchingUser,
+                    mIsSystemUser,
                     isUdfpsFingerDown,
                     userNotTrustedOrDetectionIsNeeded));
 
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/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
index b6ee4cb..3349fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.util.Range;
+import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -68,14 +69,18 @@
             @NonNull Callback settingsControllerCallback,
             SecureSettings secureSettings,
             WindowMagnificationSettings windowMagnificationSettings) {
-        mContext = context;
+        mContext = context.createWindowContext(
+                context.getDisplay(),
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                null);
+        mContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
         mDisplayId = mContext.getDisplayId();
-        mConfiguration = new Configuration(context.getResources().getConfiguration());
+        mConfiguration = new Configuration(mContext.getResources().getConfiguration());
         mSettingsControllerCallback = settingsControllerCallback;
         if (windowMagnificationSettings != null) {
             mWindowMagnificationSettings = windowMagnificationSettings;
         } else {
-            mWindowMagnificationSettings = new WindowMagnificationSettings(context,
+            mWindowMagnificationSettings = new WindowMagnificationSettings(mContext,
                     mWindowMagnificationSettingsCallback,
                     sfVsyncFrameProvider, secureSettings);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 71c5f24..6e8275f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -559,7 +559,7 @@
         final LayoutParams params = new LayoutParams(
                 LayoutParams.WRAP_CONTENT,
                 LayoutParams.WRAP_CONTENT,
-                LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+                LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSPARENT);
         params.gravity = Gravity.TOP | Gravity.START;
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/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 98a3e4b..11ef749 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -34,7 +34,6 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig;
 import com.android.systemui.surfaceeffects.ripple.RippleShader;
 import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
 import com.android.systemui.surfaceeffects.ripple.RippleView;
@@ -177,7 +176,7 @@
             mRippleView.setBlur(6.5f, 2.5f);
         } else {
             mRippleView.setDuration(CIRCLE_RIPPLE_ANIMATION_DURATION);
-            mRippleView.setColor(color, RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA);
+            mRippleView.setColor(color, RippleShader.RIPPLE_DEFAULT_ALPHA);
         }
 
         OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
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 044dd6a..75f70ae 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,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;
@@ -64,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;
@@ -169,6 +169,7 @@
             PolicyModule.class,
             PrivacyModule.class,
             QRCodeScannerModule.class,
+            QSFragmentStartableModule.class,
             ScreenshotModule.class,
             SensorModule.class,
             SecurityRepositoryModule.class,
@@ -198,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 8783ec3..29c63bb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -72,7 +72,7 @@
 
     @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")
@@ -383,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")
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/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/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 5f6098b..5f2178df 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)
@@ -223,11 +225,20 @@
     }
 
     private fun observeFaceAuthResettingConditions() {
-        // Clear auth status when keyguard is going away or when the user is switching.
-        merge(keyguardRepository.isKeyguardGoingAway, userRepository.userSwitchingInProgress)
-            .onEach { goingAwayOrUserSwitchingInProgress ->
-                if (goingAwayOrUserSwitchingInProgress) {
+        // Clear auth status when keyguard is going away or when the user is switching or device
+        // starts going to sleep.
+        merge(
+                keyguardRepository.wakefulness.map {
+                    WakefulnessModel.isSleepingOrStartingToSleep(it)
+                },
+                keyguardRepository.isKeyguardGoingAway,
+                userRepository.userSwitchingInProgress
+            )
+            .onEach { anyOfThemIsTrue ->
+                if (anyOfThemIsTrue) {
                     _isAuthenticated.value = false
+                    retryCount = 0
+                    halErrorRetryJob?.cancel()
                 }
             }
             .launchIn(applicationScope)
@@ -244,8 +255,8 @@
                     "nonStrongBiometricIsNotAllowed",
                     faceDetectLog
                 ),
-                // We don't want to run face detect if it's not possible to authenticate with FP
-                // from the bouncer. UDFPS is the only fp sensor type that won't support this.
+                // We don't want to run face detect if fingerprint can be used to unlock the device
+                // but it's not possible to authenticate with FP from the bouncer (UDFPS)
                 logAndObserve(
                     and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
                     "udfpsAuthIsNotPossibleAnymore",
@@ -302,7 +313,7 @@
                 logAndObserve(
                     combine(
                         keyguardInteractor.isSecureCameraActive,
-                        alternateBouncerInteractor.isVisible,
+                        alternateBouncerInteractor.isVisible
                     ) { a, b ->
                         !a || b
                     },
@@ -330,12 +341,12 @@
                 logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog),
                 logAndObserve(
                     deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
-                    "fpLockedOut",
+                    "fpIsNotLockedOut",
                     faceAuthLog
                 ),
                 logAndObserve(
                     trustRepository.isCurrentUserTrusted.isFalse(),
-                    "currentUserTrusted",
+                    "currentUserIsNotTrusted",
                     faceAuthLog
                 ),
                 logAndObserve(
@@ -343,11 +354,6 @@
                     "nonStrongBiometricIsAllowed",
                     faceAuthLog
                 ),
-                logAndObserve(
-                    userRepository.selectedUserInfo.map { it.isPrimary },
-                    "userIsPrimaryUser",
-                    faceAuthLog
-                ),
             )
             .reduce(::and)
             .distinctUntilChanged()
@@ -385,14 +391,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 +421,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 +590,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/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index 06ae11fe8..74ef7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -59,6 +59,7 @@
     fun onQsExpansionStared()
     fun onNotificationPanelClicked()
     fun onSwipeUpOnBouncer()
+    fun onPrimaryBouncerUserInput()
 }
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index cad40aa..5005b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -59,4 +59,5 @@
     override fun onNotificationPanelClicked() {}
 
     override fun onSwipeUpOnBouncer() {}
+    override fun onPrimaryBouncerUserInput() {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index 20ebb71..6b515da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -151,6 +151,10 @@
         return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)
     }
 
+    override fun onPrimaryBouncerUserInput() {
+        repository.cancel()
+    }
+
     /** Provide the status of face authentication */
     override val authenticationStatus = repository.authenticationStatus
 
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/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 5770f3e..ddce516 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -47,6 +47,7 @@
             duration = TO_LOCKSCREEN_DURATION,
             onStep = { value -> -translatePx + value * translatePx },
             interpolator = EMPHASIZED_DECELERATE,
+            onCancel = { 0f },
         )
     }
 
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 c2d73f3..b0fb349 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -741,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);
@@ -1343,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);
         }
     }
 
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 8f70376..aab898e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -85,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" }
@@ -173,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" }
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 f77be3a..9e204e4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -224,6 +224,8 @@
 import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -234,18 +236,12 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import kotlin.Unit;
 import kotlinx.coroutines.CoroutineDispatcher;
 
 @CentralSurfacesComponent.CentralSurfacesScope
 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;
@@ -253,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 =
@@ -868,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);
@@ -1021,7 +1011,7 @@
         mKeyguardStatusBarViewController =
                 mKeyguardStatusBarViewComponentFactory.build(
                                 mKeyguardStatusBar,
-                                mNotificationPanelViewStateProvider)
+                                mShadeViewStateProvider)
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
 
@@ -1616,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;
 
@@ -4379,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();
@@ -4418,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/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 b6a2a8a..d5a9e95 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -22,6 +22,8 @@
 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
 
 /**
@@ -147,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)
 
@@ -166,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.
@@ -183,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.
@@ -208,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,3 +287,23 @@
     /** Returns whether the screen has temporarily woken up to display notifications. */
     fun hasPulsingNotifications(): Boolean
 }
+
+/**
+ * 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/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 792939b..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 }
@@ -319,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) }
 
@@ -530,7 +530,7 @@
             } else {
                 // Let's only animate notifications
                 animationHandler = { delay: Long ->
-                    notificationPanelController.transitionToExpandedShade(delay)
+                    shadeViewController.transitionToExpandedShade(delay)
                 }
             }
             goToLockedShadeInternal(expandedView, animationHandler,
@@ -648,7 +648,7 @@
      */
     private fun performDefaultGoToFullShadeAnimation(delay: Long) {
         logger.logDefaultGoToFullShadeAnimation(delay)
-        notificationPanelController.transitionToExpandedShade(delay)
+        shadeViewController.transitionToExpandedShade(delay)
         animateAppear(delay)
     }
 
@@ -673,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/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/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/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 00928b2..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;
@@ -218,7 +218,8 @@
 
     NotificationShadeWindowViewController getNotificationShadeWindowViewController();
 
-    NotificationPanelViewController getNotificationPanelViewController();
+    /** */
+    ShadeViewController getShadeViewController();
 
     /** Get the Keyguard Message Area that displays auth messages. */
     AuthKeyguardMessageArea getKeyguardMessageArea();
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 cf2c9de..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);
                 }
             }
         }
@@ -1085,7 +1088,7 @@
                 this,
                 mStatusBarKeyguardViewManager,
                 mNotificationShadeWindowViewController,
-                mNotificationPanelViewController,
+                mShadeSurface,
                 mAmbientIndicationContainer);
         updateLightRevealScrimVisibility();
 
@@ -1271,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();
                 });
@@ -1346,7 +1349,7 @@
         mScreenOffAnimationController.initialize(this, mLightRevealScrim);
         updateLightRevealScrimVisibility();
 
-        mNotificationPanelViewController.initDependencies(
+        mShadeSurface.initDependencies(
                 this,
                 mGestureRec,
                 mShadeController::makeExpandedInvisible,
@@ -1354,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 -> {
@@ -1383,7 +1391,7 @@
                             .build());
             mBrightnessMirrorController = new BrightnessMirrorController(
                     mNotificationShadeWindowView,
-                    mNotificationPanelViewController,
+                    mShadeSurface,
                     mNotificationShadeDepthControllerLazy.get(),
                     mBrightnessSliderFactory,
                     (visible) -> {
@@ -1483,7 +1491,7 @@
                 || !mKeyguardStateController.canDismissLockScreen()
                 || mKeyguardViewMediator.isAnySimPinSecure()
                 || (mQsController.getExpanded() && trackingTouch)
-                || mNotificationPanelViewController.getBarState() == StatusBarState.SHADE_LOCKED) {
+                || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED) {
             return;
         }
 
@@ -1503,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) {
@@ -1625,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();
@@ -1700,7 +1711,7 @@
                 });
         mKeyguardViewMediator.registerCentralSurfaces(
                 /* statusBar= */ this,
-                mNotificationPanelViewController,
+                mShadeSurface,
                 mShadeExpansionStateManager,
                 mBiometricUnlockController,
                 mStackScroller,
@@ -1728,8 +1739,8 @@
     }
 
     @Override
-    public NotificationPanelViewController getNotificationPanelViewController() {
-        return mNotificationPanelViewController;
+    public ShadeViewController getShadeViewController() {
+        return mShadeSurface;
     }
 
     @Override
@@ -2086,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 */);
         }
     }
@@ -2720,7 +2731,7 @@
      */
     @Override
     public void setLockscreenUser(int newUserId) {
-        if (mLockscreenWallpaper != null) {
+        if (mLockscreenWallpaper != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
             mLockscreenWallpaper.setCurrentUser(newUserId);
         }
         mScrimController.setCurrentUser(newUserId);
@@ -2746,8 +2757,8 @@
             mStatusBarWindowController.refreshStatusBarHeight();
         }
 
-        if (mNotificationPanelViewController != null) {
-            mNotificationPanelViewController.updateResources();
+        if (mShadeSurface != null) {
+            mShadeSurface.updateResources();
         }
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.updateResources();
@@ -3005,7 +3016,7 @@
     public void showKeyguardImpl() {
         Trace.beginSection("CentralSurfaces#showKeyguard");
         if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
-            mNotificationPanelViewController.cancelAnimation();
+            mShadeSurface.cancelAnimation();
             onLaunchTransitionFadingEnded();
         }
         mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
@@ -3024,7 +3035,7 @@
     }
 
     private void onLaunchTransitionFadingEnded() {
-        mNotificationPanelViewController.resetAlpha();
+        mShadeSurface.resetAlpha();
         mCameraLauncherLazy.get().setLaunchingAffordance(false);
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
@@ -3054,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(),
@@ -3087,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() {
@@ -3127,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();
         }
 
@@ -3139,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();
@@ -3243,7 +3254,7 @@
         boolean animate = (!mDozing && shouldAnimateDozeWakeup())
                 || (mDozing && mDozeParameters.shouldControlScreenOff() && keyguardVisibleOrWillBe);
 
-        mNotificationPanelViewController.setDozing(mDozing, animate);
+        mShadeSurface.setDozing(mDozing, animate);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -3323,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;
@@ -3508,8 +3519,8 @@
         if (mPhoneStatusBarViewController != null) {
             mPhoneStatusBarViewController.setImportantForAccessibility(importance);
         }
-        mNotificationPanelViewController.setImportantForAccessibility(importance);
-        mNotificationPanelViewController.setBouncerShowing(bouncerShowing);
+        mShadeSurface.setImportantForAccessibility(importance);
+        mShadeSurface.setBouncerShowing(bouncerShowing);
     }
 
     /**
@@ -3517,7 +3528,7 @@
      */
     @Override
     public void collapseShade() {
-        if (mNotificationPanelViewController.isTracking()) {
+        if (mShadeSurface.isTracking()) {
             mNotificationShadeWindowViewController.cancelCurrentTouch();
         }
         if (mPanelExpanded && mState == StatusBarState.SHADE) {
@@ -3621,7 +3632,7 @@
                     }
                 }
 
-                mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(
+                mShadeSurface.setWillPlayDelayedDozeAmountAnimation(
                         mShouldDelayWakeUpAnimation);
                 mWakeUpCoordinator.setWakingUp(
                         /* wakingUp= */ true,
@@ -3663,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) {
@@ -3699,7 +3710,7 @@
                 !mDozeParameters.shouldControlScreenOff(), !mDeviceInteractive,
                 !mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive());
 
-        mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
+        mShadeSurface.setTouchAndAnimationDisabled(disabled);
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
 
@@ -3707,7 +3718,7 @@
         @Override
         public void onScreenTurningOn() {
             mFalsingCollector.onScreenTurningOn();
-            mNotificationPanelViewController.onScreenTurningOn();
+            mShadeSurface.onScreenTurningOn();
         }
 
         @Override
@@ -4351,7 +4362,7 @@
             }
             // We need the new R.id.keyguard_indication_area before recreating
             // mKeyguardIndicationController
-            mNotificationPanelViewController.onThemeChanged();
+            mShadeSurface.onThemeChanged();
 
             if (mStatusBarKeyguardViewManager != null) {
                 mStatusBarKeyguardViewManager.onThemeChanged();
@@ -4397,7 +4408,7 @@
                     mNavigationBarController.touchAutoDim(mDisplayId);
                     Trace.beginSection("CentralSurfaces#updateKeyguardState");
                     if (mState == StatusBarState.KEYGUARD) {
-                        mNotificationPanelViewController.cancelPendingCollapse();
+                        mShadeSurface.cancelPendingCollapse();
                     }
                     updateDozingState();
                     checkBarModes();
@@ -4423,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/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 7fc0996..dfaee4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -36,9 +36,9 @@
 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;
@@ -82,7 +82,7 @@
     private final NotificationMediaManager mMediaManager;
     private final NotificationGutsManager mGutsManager;
 
-    private final NotificationPanelViewController mNotificationPanel;
+    private final ShadeViewController mNotificationPanel;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final AboveShelfObserver mAboveShelfObserver;
     private final DozeScrimController mDozeScrimController;
@@ -105,7 +105,7 @@
     @Inject
     StatusBarNotificationPresenter(
             Context context,
-            NotificationPanelViewController panel,
+            ShadeViewController panel,
             QuickSettingsController quickSettingsController,
             HeadsUpManagerPhone headsUp,
             NotificationShadeWindowView statusBarWindow,
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 b96001f..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
@@ -21,7 +21,9 @@
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.ViewStub;
+
 import androidx.constraintlayout.motion.widget.MotionLayout;
+
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.LockIconView;
 import com.android.systemui.R;
@@ -42,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;
@@ -74,11 +77,14 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.settings.SecureSettings;
+
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoSet;
+
 import java.util.concurrent.Executor;
+
 import javax.inject.Named;
 import javax.inject.Provider;
 
@@ -160,6 +166,12 @@
     }
 
     /** */
+    @Binds
+    @CentralSurfacesComponent.CentralSurfacesScope
+    abstract ShadeViewController bindsShadeViewController(
+            NotificationPanelViewController notificationPanelViewController);
+
+    /** */
     @Provides
     @CentralSurfacesComponent.CentralSurfacesScope
     public static LockIconView getLockIconView(
@@ -298,7 +310,7 @@
             StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             KeyguardStateController keyguardStateController,
-            NotificationPanelViewController notificationPanelViewController,
+            ShadeViewController shadeViewController,
             StatusBarStateController statusBarStateController,
             CommandQueue commandQueue,
             CarrierConfigTracker carrierConfigTracker,
@@ -321,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/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..2962c14 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;
@@ -290,7 +291,7 @@
         when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
 
         when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
-        when(mUserManager.isPrimaryUser()).thenReturn(true);
+        currentUserIsSystem();
         when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
         when(mStrongAuthTracker
                 .isUnlockingWithBiometricAllowed(anyBoolean() /* isClass3Biometric */))
@@ -303,6 +304,7 @@
 
         mMockitoSession = ExtendedMockito.mockitoSession()
                 .spyStatic(SubscriptionManager.class)
+                .strictness(Strictness.WARN)
                 .startMocking();
         ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                 .when(SubscriptionManager::getDefaultSubscriptionId);
@@ -958,7 +960,7 @@
     public void requestFaceAuth_whenFaceAuthWasStarted_returnsTrue() throws RemoteException {
         // This satisfies all the preconditions to run face auth.
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1465,7 +1467,7 @@
 
         // Preconditions for sfps auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1501,7 +1503,7 @@
 
         // GIVEN Preconditions for sfps auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1530,7 +1532,7 @@
 
         // GIVEN Preconditions for sfps auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1682,7 +1684,7 @@
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
         occludingAppRequestsFaceAuth();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         primaryAuthNotRequiredByStrongAuthTracker();
         biometricsEnabledForCurrentUser();
         currentUserDoesNotHaveTrust();
@@ -1703,7 +1705,7 @@
         // Face auth should run when the following is true.
         bouncerFullyVisibleAndNotGoingToSleep();
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         primaryAuthNotRequiredByStrongAuthTracker();
         biometricsEnabledForCurrentUser();
         currentUserDoesNotHaveTrust();
@@ -1726,7 +1728,7 @@
         // Face auth should run when the following is true.
         bouncerFullyVisibleAndNotGoingToSleep();
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         primaryAuthNotRequiredByStrongAuthTracker();
         biometricsEnabledForCurrentUser();
         currentUserDoesNotHaveTrust();
@@ -1747,7 +1749,7 @@
     public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
         cleanupKeyguardUpdateMonitor();
         // This disables face auth
-        when(mUserManager.isPrimaryUser()).thenReturn(false);
+        when(mUserManager.isSystemUser()).thenReturn(false);
         mKeyguardUpdateMonitor =
                 new TestableKeyguardUpdateMonitor(mContext);
 
@@ -1771,7 +1773,7 @@
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         biometricsEnabledForCurrentUser();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
@@ -1789,7 +1791,7 @@
             throws RemoteException {
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1811,7 +1813,7 @@
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1831,7 +1833,7 @@
             throws RemoteException {
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1852,7 +1854,7 @@
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1874,7 +1876,7 @@
             throws RemoteException {
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1894,7 +1896,7 @@
             throws RemoteException {
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1913,7 +1915,7 @@
     public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1938,7 +1940,7 @@
     public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1957,7 +1959,7 @@
             throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -1975,7 +1977,7 @@
             throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -2000,7 +2002,7 @@
             throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -2322,7 +2324,7 @@
             throws RemoteException {
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -2453,7 +2455,7 @@
         mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -2477,7 +2479,7 @@
         mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN;
         keyguardNotGoingAway();
         bouncerFullyVisibleAndNotGoingToSleep();
-        currentUserIsPrimary();
+        currentUserIsSystem();
         currentUserDoesNotHaveTrust();
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
@@ -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
@@ -2888,8 +2877,8 @@
                         new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
     }
 
-    private void currentUserIsPrimary() {
-        when(mUserManager.isPrimaryUser()).thenReturn(true);
+    private void currentUserIsSystem() {
+        when(mUserManager.isSystemUser()).thenReturn(true);
     }
 
     private void biometricsNotDisabledThroughDevicePolicyManager() {
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/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index fc75d47..fa40fc4 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
@@ -71,7 +72,6 @@
 import java.io.PrintWriter
 import java.io.StringWriter
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -541,14 +541,6 @@
         }
 
     @Test
-    fun authenticateDoesNotRunWhenCurrentUserIsNotPrimary() =
-        testScope.runTest {
-            testGatingCheckForFaceAuth {
-                launch { fakeUserRepository.setSelectedUserInfo(secondaryUser) }
-            }
-        }
-
-    @Test
     fun authenticateDoesNotRunWhenSecureCameraIsActive() =
         testScope.runTest {
             testGatingCheckForFaceAuth {
@@ -652,6 +644,58 @@
         }
 
     @Test
+    fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            keyguardRepository.setWakefulnessModel(
+                WakefulnessModel(
+                    WakefulnessState.STARTING_TO_SLEEP,
+                    isWakingUpOrAwake = false,
+                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                    lastSleepReason = WakeSleepReason.POWER_BUTTON
+                )
+            )
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsResetToFalseWhenDeviceGoesToSleep() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            keyguardRepository.setWakefulnessModel(
+                WakefulnessModel(
+                    WakefulnessState.ASLEEP,
+                    isWakingUpOrAwake = false,
+                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                    lastSleepReason = WakeSleepReason.POWER_BUTTON
+                )
+            )
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
     fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() =
         testScope.runTest {
             initCollectors()
@@ -824,6 +868,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/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 3d1d2f4..5da1a84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -279,6 +279,23 @@
         }
 
     @Test
+    fun faceAuthIsCancelledWhenUserInputOnPrimaryBouncer() =
+        testScope.runTest {
+            underTest.start()
+
+            underTest.onSwipeUpOnBouncer()
+
+            runCurrent()
+            assertThat(faceAuthRepository.isAuthRunning.value).isTrue()
+
+            underTest.onPrimaryBouncerUserInput()
+
+            runCurrent()
+
+            assertThat(faceAuthRepository.isAuthRunning.value).isFalse()
+        }
+
+    @Test
     fun faceAuthIsRequestedWhenSwipeUpOnBouncer() =
         testScope.runTest {
             underTest.start()
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/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 0c4e845..efa5f0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -92,6 +92,21 @@
             job.cancel()
         }
 
+    @Test
+    fun lockscreenTranslationYResettedAfterJobCancelled() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val pixels = 100
+            val job =
+                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+            repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
+
+            assertThat(values.last()).isEqualTo(0f)
+
+            job.cancel()
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
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/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index a03bc1e..7dc622b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -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()
@@ -344,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()
 
@@ -384,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(
@@ -717,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/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/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 589c8bc..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
@@ -561,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;
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/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 c16d140..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
@@ -38,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;
@@ -107,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,
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/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/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/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/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b042c30..ff72476 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -397,7 +397,7 @@
                             setUpPipes();
                             mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
                                     FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
-                                            ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+                                            ? ApplicationThreadConstants.BACKUP_MODE_RESTORE
                                             : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL,
                                     mBackupEligibilityRules.getBackupDestination());
                             mAgentPackage = pkg;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 1656b6f..77990af 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -677,7 +677,7 @@
         // Good to go!  Set up and bind the agent...
         mAgent = backupManagerService.bindToAgentSynchronous(
                 mCurrentPackage.applicationInfo,
-                ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL,
+                ApplicationThreadConstants.BACKUP_MODE_RESTORE,
                 mBackupEligibilityRules.getBackupDestination());
         if (mAgent == null) {
             Slog.w(TAG, "Can't find backup agent for " + packageName);
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..1f80aec 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;
             }
         }
     }
@@ -4598,8 +4603,7 @@
             boolean isRestrictedBackupMode = false;
             if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
                 isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
-                        && ((backupTarget.backupMode == BackupRecord.RESTORE)
-                                || (backupTarget.backupMode == BackupRecord.RESTORE_FULL)
+                        && ((backupTarget.backupMode == BackupRecord.RESTORE_FULL)
                                 || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
             }
 
@@ -7311,7 +7315,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);
         }
@@ -13382,7 +13393,8 @@
 
             BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination);
             ComponentName hostingName =
-                    (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL)
+                    (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+                            || backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE)
                             ? new ComponentName(app.packageName, app.backupAgentName)
                             : new ComponentName("android", "FullBackupAgent");
 
@@ -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/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a3163e0..02c4770 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2284,8 +2284,8 @@
                 synchronized (VolumeStreamState.class) {
                     mStreamStates[AudioSystem.STREAM_DTMF]
                             .setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
-                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
-                            System.VOLUME_SETTINGS_INT[a11yStreamAlias];
+                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setSettingName(
+                            System.VOLUME_SETTINGS_INT[a11yStreamAlias]);
                     mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
                             mStreamStates[a11yStreamAlias], caller);
                 }
@@ -7700,7 +7700,7 @@
         private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
         private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
         private boolean mIsMuted = false;
-        private final String mSettingName;
+        private String mSettingName;
 
         // No API in AudioSystem to get a device from strategy or from attributes.
         // Need a valid public stream type to use current API getDeviceForStream
@@ -8029,15 +8029,19 @@
         }
 
         private void persistVolumeGroup(int device) {
-            if (mUseFixedVolume) {
+            // No need to persist the index if the volume group is backed up
+            // by a public stream type as this is redundant
+            if (mUseFixedVolume || mHasValidStreamType) {
                 return;
             }
             if (DEBUG_VOL) {
                 Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
                         + mAudioVolumeGroup.name()
                         + ", device " + AudioSystem.getOutputDeviceName(device)
-                        + " and User=" + getCurrentUserId());
+                        + " and User=" + getCurrentUserId()
+                        + " mSettingName: " + mSettingName);
             }
+
             boolean success = mSettings.putSystemIntForUser(mContentResolver,
                     getSettingNameForDevice(device),
                     getIndex(device),
@@ -8100,6 +8104,14 @@
             return mSettingName + "_" + AudioSystem.getOutputDeviceName(device);
         }
 
+        void setSettingName(String settingName) {
+            mSettingName = settingName;
+        }
+
+        String getSettingName() {
+            return mSettingName;
+        }
+
         private void dump(PrintWriter pw) {
             pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
             pw.print("   Muted: ");
@@ -8242,6 +8254,9 @@
          */
         public void setVolumeGroupState(VolumeGroupState volumeGroupState) {
             mVolumeGroupState = volumeGroupState;
+            if (mVolumeGroupState != null) {
+                mVolumeGroupState.setSettingName(mVolumeIndexSettingName);
+            }
         }
         /**
          * Update the minimum index that can be used without MODIFY_AUDIO_SETTINGS permission
@@ -8315,6 +8330,17 @@
             return (mVolumeIndexSettingName != null && !mVolumeIndexSettingName.isEmpty());
         }
 
+        void setSettingName(String settingName) {
+            mVolumeIndexSettingName = settingName;
+            if (mVolumeGroupState != null) {
+                mVolumeGroupState.setSettingName(mVolumeIndexSettingName);
+            }
+        }
+
+        String getSettingName() {
+            return mVolumeIndexSettingName;
+        }
+
         public void readSettings() {
             synchronized (mSettingsLock) {
                 synchronized (VolumeStreamState.class) {
@@ -8989,7 +9015,7 @@
             if (streamState.hasValidSettingsName()) {
                 mSettings.putSystemIntForUser(mContentResolver,
                         streamState.getSettingNameForDevice(device),
-                        (streamState.getIndex(device) + 5)/ 10,
+                        (streamState.getIndex(device) + 5) / 10,
                         UserHandle.USER_CURRENT);
             }
         }
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/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1f4c7e6..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 */
@@ -3747,15 +3807,23 @@
             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.
-                startOrMigrateIkeSession(mActiveNetwork);
+                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
@@ -4033,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.
@@ -4470,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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 85b4034..5d92c7f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -119,7 +119,6 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IntArray;
@@ -298,11 +297,10 @@
             mDisplayWindowPolicyControllers = new SparseArray<>();
 
     /**
-     *  Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
-     *  {@link DisplayDevice#mUniqueId}.
+     * Provides {@link HighBrightnessModeMetadata}s for {@link DisplayDevice}s.
      */
-    public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
-            new ArrayMap<>();
+    private final HighBrightnessModeMetadataMapper mHighBrightnessModeMetadataMapper =
+            new HighBrightnessModeMetadataMapper();
 
     // List of all currently registered display adapters.
     private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1823,19 +1821,14 @@
 
         DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-            if (device == null) {
-                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
-                        + display.getDisplayIdLocked());
-                return;
-            }
-
             final int leadDisplayId = display.getLeadDisplayIdLocked();
             updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
 
-            final String uniqueId = device.getUniqueId();
-            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
-            dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+            HighBrightnessModeMetadata hbmMetadata =
+                    mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+            if (hbmMetadata != null) {
+                dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+            }
         }
     }
 
@@ -1922,19 +1915,14 @@
         final int displayId = display.getDisplayIdLocked();
         final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-            if (device == null) {
-                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
-                        + display.getDisplayIdLocked());
-                return;
-            }
-
             final int leadDisplayId = display.getLeadDisplayIdLocked();
             updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
 
-            final String uniqueId = device.getUniqueId();
-            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
-            dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+            HighBrightnessModeMetadata hbmMetadata =
+                    mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+            if (hbmMetadata != null) {
+                dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+            }
         }
     }
 
@@ -3073,26 +3061,6 @@
         mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
     }
 
-    private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
-        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-        if (device == null) {
-            Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
-                    + display.getDisplayIdLocked());
-            return null;
-        }
-
-        final String uniqueId = device.getUniqueId();
-
-        if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
-            return mHighBrightnessModeMetadataMap.get(uniqueId);
-        }
-
-        // HBM Time info not present. Create a new one for this physical display.
-        HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
-        mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
-        return hbmInfo;
-    }
-
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     private void addDisplayPowerControllerLocked(LogicalDisplay display) {
         if (mPowerHandler == null) {
@@ -3113,7 +3081,13 @@
         // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
         // displayPowerController, so the hbm info can be correctly associated
         // with the corresponding displaydevice.
-        HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
+        HighBrightnessModeMetadata hbmMetadata =
+                mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+        if (hbmMetadata == null) {
+            Slog.wtf(TAG, "High Brightness Mode Metadata is null in DisplayManagerService for "
+                    + "display: " + display.getDisplayIdLocked());
+            return;
+        }
         if (DeviceConfig.getBoolean("display_manager",
                 "use_newly_structured_display_power_controller", true)) {
             displayPowerController = new DisplayPowerController2(
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/HighBrightnessModeMetadataMapper.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadataMapper.java
new file mode 100644
index 0000000..76702d3
--- /dev/null
+++ b/services/core/java/com/android/server/display/HighBrightnessModeMetadataMapper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.server.display;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+
+/**
+ * Provides {@link HighBrightnessModeMetadata}s for {@link DisplayDevice}s. This class should only
+ * be accessed from the display thread.
+ */
+class HighBrightnessModeMetadataMapper {
+
+    private static final String TAG = "HighBrightnessModeMetadataMapper";
+
+    /**
+     *  Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
+     *  {@link DisplayDevice#mUniqueId}.
+     */
+    private final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
+            new ArrayMap<>();
+
+    HighBrightnessModeMetadata getHighBrightnessModeMetadataLocked(LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        if (device == null) {
+            Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+                    + display.getDisplayIdLocked());
+            return null;
+        }
+
+        final String uniqueId = device.getUniqueId();
+
+        if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
+            return mHighBrightnessModeMetadataMap.get(uniqueId);
+        }
+
+        // HBM Time info not present. Create a new one for this physical display.
+        HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
+        mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
+        return hbmInfo;
+    }
+}
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/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 9eedc4e..f47c4b2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -682,7 +682,6 @@
     @ServiceThreadOnly
     private void launchDeviceDiscovery() {
         assertRunOnServiceThread();
-        clearDeviceInfoList();
         DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
                 new DeviceDiscoveryCallback() {
                     @Override
@@ -691,13 +690,6 @@
                             mService.getHdmiCecNetwork().addCecDevice(info);
                         }
 
-                        // Since we removed all devices when it starts and
-                        // device discovery action does not poll local devices,
-                        // we should put device info of local device manually here
-                        for (HdmiCecLocalDevice device : mService.getAllCecLocalDevices()) {
-                            mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
-                        }
-
                         mSelectRequestBuffer.process();
                         resetSelectRequestBuffer();
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 805ff66..75fe63a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1267,6 +1267,7 @@
         // It's now safe to flush existing local devices from mCecController since they were
         // already moved to 'localDevices'.
         clearCecLocalDevices();
+        mHdmiCecNetwork.clearDeviceList();
         allocateLogicalAddress(localDevices, initiatedBy);
     }
 
@@ -1303,6 +1304,7 @@
                                         HdmiControlManager.POWER_STATUS_ON, getCecVersion());
                                 localDevice.setDeviceInfo(deviceInfo);
                                 mHdmiCecNetwork.addLocalDevice(deviceType, localDevice);
+                                mHdmiCecNetwork.addCecDevice(localDevice.getDeviceInfo());
                                 mCecController.addLogicalAddress(logicalAddress);
                                 allocatedDevices.add(localDevice);
                             }
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/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 0ae1e80..a1b67e1 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -18,14 +18,19 @@
 
 import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
 
+import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
 import static com.android.server.EventLogTags.IMF_HIDE_IME;
 import static com.android.server.EventLogTags.IMF_SHOW_IME;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.ResultReceiver;
@@ -38,6 +43,7 @@
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.server.LocalServices;
+import com.android.server.wm.ImeTargetVisibilityPolicy;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.util.Objects;
@@ -56,10 +62,14 @@
 
     private final WindowManagerInternal mWindowManagerInternal;
 
+    @NonNull
+    private final ImeTargetVisibilityPolicy mImeTargetVisibilityPolicy;
+
 
     DefaultImeVisibilityApplier(InputMethodManagerService service) {
         mService = service;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+        mImeTargetVisibilityPolicy = LocalServices.getService(ImeTargetVisibilityPolicy.class);
     }
 
     @GuardedBy("ImfLock.class")
@@ -162,8 +172,37 @@
                 mService.showCurrentInputLocked(windowToken, statsToken,
                         InputMethodManager.SHOW_IMPLICIT, null, reason);
                 break;
+            case STATE_SHOW_IME_SNAPSHOT:
+                showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null);
+                break;
+            case STATE_REMOVE_IME_SNAPSHOT:
+                removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
+                break;
             default:
                 throw new IllegalArgumentException("Invalid IME visibility state: " + state);
         }
     }
+
+    @GuardedBy("ImfLock.class")
+    @Override
+    public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
+            @Nullable ImeTracker.Token statsToken) {
+        if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
+            mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
+                    SHOW_IME_SCREENSHOT_FROM_IMMS, statsToken);
+            return true;
+        }
+        return false;
+    }
+
+    @GuardedBy("ImfLock.class")
+    @Override
+    public boolean removeImeScreenshot(int displayId) {
+        if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
+            mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow,
+                    REMOVE_IME_SCREENSHOT_FROM_IMMS, null);
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index f03e985..27f6a89 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -16,6 +16,7 @@
 
 package com.android.server.inputmethod;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.ResultReceiver;
@@ -76,4 +77,27 @@
         // TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget
         //  here to end up updating IME layering after IMMS#attachNewInputLocked called.
     }
+
+    /**
+     * Shows the IME screenshot and attach it to the given IME target window.
+     *
+     * @param windowToken The token of a window to show the IME screenshot.
+     * @param displayId The unique id to identify the display
+     * @param statsToken  A token that tracks the progress of an IME request.
+     * @return {@code true} if success, {@code false} otherwise.
+     */
+    default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId,
+            @Nullable ImeTracker.Token statsToken) {
+        return false;
+    }
+
+    /**
+     * Removes the IME screenshot on the given display.
+     *
+     * @param displayId The target display of showing IME screenshot.
+     * @return {@code true} if success, {@code false} otherwise.
+     */
+    default boolean removeImeScreenshot(int displayId) {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 61fe654..19d6fa0 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -29,6 +29,8 @@
 import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 
 import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
 import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;
 
 import android.accessibilityservice.AccessibilityService;
@@ -49,6 +51,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.server.LocalServices;
+import com.android.server.wm.ImeTargetChangeListener;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.PrintWriter;
@@ -99,6 +102,18 @@
      */
     private boolean mInputShown;
 
+    /**
+     * Set if we called
+     * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}.
+     */
+    private boolean mRequestedImeScreenshot;
+
+    /** The window token of the current visible IME layering target overlay. */
+    private IBinder mCurVisibleImeLayeringOverlay;
+
+    /** The window token of the current visible IME input target. */
+    private IBinder mCurVisibleImeInputTarget;
+
     /** Represent the invalid IME visibility state */
     public static final int STATE_INVALID = -1;
 
@@ -122,6 +137,10 @@
     public static final int STATE_HIDE_IME_NOT_ALWAYS = 6;
 
     public static final int STATE_SHOW_IME_IMPLICIT = 7;
+
+    /** State to handle removing an IME preview surface when necessary. */
+    public static final int STATE_REMOVE_IME_SNAPSHOT = 8;
+
     @IntDef({
             STATE_INVALID,
             STATE_HIDE_IME,
@@ -132,6 +151,7 @@
             STATE_HIDE_IME_EXPLICIT,
             STATE_HIDE_IME_NOT_ALWAYS,
             STATE_SHOW_IME_IMPLICIT,
+            STATE_REMOVE_IME_SNAPSHOT,
     })
     @interface VisibilityState {}
 
@@ -172,6 +192,24 @@
         mWindowManagerInternal = wmService;
         mImeDisplayValidator = imeDisplayValidator;
         mPolicy = imePolicy;
+        mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() {
+            @Override
+            public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken,
+                    boolean visible, boolean removed) {
+                mCurVisibleImeLayeringOverlay = (visible && !removed) ? overlayWindowToken : null;
+            }
+
+            @Override
+            public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
+                    boolean visibleRequested, boolean removed) {
+                mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null;
+                if (mCurVisibleImeInputTarget == null && mCurVisibleImeLayeringOverlay != null) {
+                    mService.onApplyImeVisibilityFromComputer(imeInputTarget,
+                            new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
+                                    SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE));
+                }
+            }
+        });
     }
 
     /**
@@ -453,6 +491,21 @@
         return null;
     }
 
+    @VisibleForTesting
+    ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) {
+        final ImeTargetWindowState state = getWindowStateOrNull(windowToken);
+        if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) {
+            mRequestedImeScreenshot = true;
+            return new ImeVisibilityResult(STATE_SHOW_IME_SNAPSHOT, SHOW_IME_SCREENSHOT_FROM_IMMS);
+        }
+        if (interactive && mRequestedImeScreenshot) {
+            mRequestedImeScreenshot = false;
+            return new ImeVisibilityResult(STATE_REMOVE_IME_SNAPSHOT,
+                    REMOVE_IME_SCREENSHOT_FROM_IMMS);
+        }
+        return null;
+    }
+
     IBinder getWindowTokenFrom(IBinder requestImeToken) {
         for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
             final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2433211..c70d555 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4847,6 +4847,14 @@
         }
     }
 
+    void onApplyImeVisibilityFromComputer(IBinder windowToken,
+            @NonNull ImeVisibilityResult result) {
+        synchronized (ImfLock.class) {
+            mVisibilityApplier.applyImeVisibility(windowToken, null, result.getState(),
+                    result.getReason());
+        }
+    }
+
     @GuardedBy("ImfLock.class")
     void setEnabledSessionLocked(SessionState session) {
         if (mEnabledSession != session) {
@@ -5083,6 +5091,14 @@
                 return;
             }
             if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) {
+                // Handle IME visibility when interactive changed before finishing the input to
+                // ensure we preserve the last state as possible.
+                final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
+                        mCurFocusedWindow, interactive);
+                if (imeVisRes != null) {
+                    mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null,
+                            imeVisRes.getState(), imeVisRes.getReason());
+                }
                 // Eligible IME processes use new "setInteractive" protocol.
                 mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode);
             } else {
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ebcbfed..07891f3 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -10796,7 +10796,8 @@
         static final String FLAG_SEPARATOR = "\\|";
 
         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
-        ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
+        @GuardedBy("mRequestedNotificationListeners")
+        private final ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
                 mRequestedNotificationListeners = new ArrayMap<>();
         private final boolean mIsHeadlessSystemUserMode;
 
@@ -10914,9 +10915,11 @@
         @Override
         public void onUserRemoved(int user) {
             super.onUserRemoved(user);
-            for (int i = mRequestedNotificationListeners.size() - 1; i >= 0; i--) {
-                if (mRequestedNotificationListeners.keyAt(i).second == user) {
-                    mRequestedNotificationListeners.removeAt(i);
+            synchronized (mRequestedNotificationListeners) {
+                for (int i = mRequestedNotificationListeners.size() - 1; i >= 0; i--) {
+                    if (mRequestedNotificationListeners.keyAt(i).second == user) {
+                        mRequestedNotificationListeners.removeAt(i);
+                    }
                 }
             }
         }
@@ -10925,31 +10928,34 @@
         public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
             super.onPackagesChanged(removingPackage, pkgList, uidList);
 
-            // Since the default behavior is to allow everything, we don't need to explicitly
-            // handle package add or update. they will be added to the xml file on next boot or
-            // when the user tries to change the settings.
-            if (removingPackage) {
-                for (int i = 0; i < pkgList.length; i++) {
-                    String pkg = pkgList[i];
-                    int userId = UserHandle.getUserId(uidList[i]);
-                    for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
-                        Pair<ComponentName, Integer> key = mRequestedNotificationListeners.keyAt(j);
-                        if (key.second == userId && key.first.getPackageName().equals(pkg)) {
-                            mRequestedNotificationListeners.removeAt(j);
+            synchronized (mRequestedNotificationListeners) {
+                // Since the default behavior is to allow everything, we don't need to explicitly
+                // handle package add or update. they will be added to the xml file on next boot or
+                // when the user tries to change the settings.
+                if (removingPackage) {
+                    for (int i = 0; i < pkgList.length; i++) {
+                        String pkg = pkgList[i];
+                        int userId = UserHandle.getUserId(uidList[i]);
+                        for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+                            Pair<ComponentName, Integer> key =
+                                    mRequestedNotificationListeners.keyAt(j);
+                            if (key.second == userId && key.first.getPackageName().equals(pkg)) {
+                                mRequestedNotificationListeners.removeAt(j);
+                            }
                         }
                     }
                 }
-            }
 
-            // clean up anything in the disallowed pkgs list
-            for (int i = 0; i < pkgList.length; i++) {
-                String pkg = pkgList[i];
-                int userId = UserHandle.getUserId(uidList[i]);
-                for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
-                    NotificationListenerFilter nlf = mRequestedNotificationListeners.valueAt(j);
+                // clean up anything in the disallowed pkgs list
+                for (int i = 0; i < pkgList.length; i++) {
+                    String pkg = pkgList[i];
+                    for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+                        NotificationListenerFilter nlf =
+                                mRequestedNotificationListeners.valueAt(j);
 
-                    VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
-                    nlf.removePackage(ai);
+                        VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+                        nlf.removePackage(ai);
+                    }
                 }
             }
         }
@@ -10997,7 +11003,9 @@
                     }
                     NotificationListenerFilter nlf =
                             new NotificationListenerFilter(approved, disallowedPkgs);
-                    mRequestedNotificationListeners.put(Pair.create(cn, userId), nlf);
+                    synchronized (mRequestedNotificationListeners) {
+                        mRequestedNotificationListeners.put(Pair.create(cn, userId), nlf);
+                    }
                 }
             }
         }
@@ -11005,72 +11013,81 @@
         @Override
         protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
             out.startTag(null, TAG_REQUESTED_LISTENERS);
-            for (Pair<ComponentName, Integer> listener : mRequestedNotificationListeners.keySet()) {
-                NotificationListenerFilter nlf = mRequestedNotificationListeners.get(listener);
-                out.startTag(null, TAG_REQUESTED_LISTENER);
-                XmlUtils.writeStringAttribute(
-                        out, ATT_COMPONENT, listener.first.flattenToString());
-                XmlUtils.writeIntAttribute(out, ATT_USER_ID, listener.second);
+            synchronized (mRequestedNotificationListeners) {
+                for (Pair<ComponentName, Integer> listener :
+                        mRequestedNotificationListeners.keySet()) {
+                    NotificationListenerFilter nlf = mRequestedNotificationListeners.get(listener);
+                    out.startTag(null, TAG_REQUESTED_LISTENER);
+                    XmlUtils.writeStringAttribute(
+                            out, ATT_COMPONENT, listener.first.flattenToString());
+                    XmlUtils.writeIntAttribute(out, ATT_USER_ID, listener.second);
 
-                out.startTag(null, TAG_APPROVED);
-                XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
-                out.endTag(null, TAG_APPROVED);
+                    out.startTag(null, TAG_APPROVED);
+                    XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
+                    out.endTag(null, TAG_APPROVED);
 
-                for (VersionedPackage ai : nlf.getDisallowedPackages()) {
-                    if (!TextUtils.isEmpty(ai.getPackageName())) {
-                        out.startTag(null, TAG_DISALLOWED);
-                        XmlUtils.writeStringAttribute(out, ATT_PKG, ai.getPackageName());
-                        XmlUtils.writeIntAttribute(out, ATT_UID, ai.getVersionCode());
-                        out.endTag(null, TAG_DISALLOWED);
+                    for (VersionedPackage ai : nlf.getDisallowedPackages()) {
+                        if (!TextUtils.isEmpty(ai.getPackageName())) {
+                            out.startTag(null, TAG_DISALLOWED);
+                            XmlUtils.writeStringAttribute(out, ATT_PKG, ai.getPackageName());
+                            XmlUtils.writeIntAttribute(out, ATT_UID, ai.getVersionCode());
+                            out.endTag(null, TAG_DISALLOWED);
+                        }
                     }
-                }
 
-                out.endTag(null, TAG_REQUESTED_LISTENER);
+                    out.endTag(null, TAG_REQUESTED_LISTENER);
+                }
             }
 
             out.endTag(null, TAG_REQUESTED_LISTENERS);
         }
 
-        protected @Nullable NotificationListenerFilter getNotificationListenerFilter(
+        @Nullable protected NotificationListenerFilter getNotificationListenerFilter(
                 Pair<ComponentName, Integer> pair) {
-            return mRequestedNotificationListeners.get(pair);
+            synchronized (mRequestedNotificationListeners) {
+                return mRequestedNotificationListeners.get(pair);
+            }
         }
 
         protected void setNotificationListenerFilter(Pair<ComponentName, Integer> pair,
                 NotificationListenerFilter nlf) {
-            mRequestedNotificationListeners.put(pair, nlf);
+            synchronized (mRequestedNotificationListeners) {
+                mRequestedNotificationListeners.put(pair, nlf);
+            }
         }
 
         @Override
         protected void ensureFilters(ServiceInfo si, int userId) {
-            Pair listener = Pair.create(si.getComponentName(), userId);
-            NotificationListenerFilter existingNlf =
-                    mRequestedNotificationListeners.get(listener);
-            if (si.metaData != null) {
-                if (existingNlf  == null) {
-                    // no stored filters for this listener; see if they provided a default
-                    if (si.metaData.containsKey(META_DATA_DEFAULT_FILTER_TYPES)) {
-                        String typeList =
-                                si.metaData.get(META_DATA_DEFAULT_FILTER_TYPES).toString();
-                        if (typeList != null) {
-                            int types = getTypesFromStringList(typeList);
-                            NotificationListenerFilter nlf =
-                                    new NotificationListenerFilter(types, new ArraySet<>());
-                            mRequestedNotificationListeners.put(listener, nlf);
+            Pair<ComponentName, Integer> listener = Pair.create(si.getComponentName(), userId);
+            synchronized (mRequestedNotificationListeners) {
+                NotificationListenerFilter existingNlf =
+                        mRequestedNotificationListeners.get(listener);
+                if (si.metaData != null) {
+                    if (existingNlf == null) {
+                        // no stored filters for this listener; see if they provided a default
+                        if (si.metaData.containsKey(META_DATA_DEFAULT_FILTER_TYPES)) {
+                            String typeList =
+                                    si.metaData.get(META_DATA_DEFAULT_FILTER_TYPES).toString();
+                            if (typeList != null) {
+                                int types = getTypesFromStringList(typeList);
+                                NotificationListenerFilter nlf =
+                                        new NotificationListenerFilter(types, new ArraySet<>());
+                                mRequestedNotificationListeners.put(listener, nlf);
+                            }
                         }
                     }
-                }
 
-                // also check the types they never want bridged
-                if (si.metaData.containsKey(META_DATA_DISABLED_FILTER_TYPES)) {
-                    int neverBridge = getTypesFromStringList(si.metaData.get(
-                            META_DATA_DISABLED_FILTER_TYPES).toString());
-                    if (neverBridge != 0) {
-                        NotificationListenerFilter nlf =
-                                mRequestedNotificationListeners.getOrDefault(
-                                        listener, new NotificationListenerFilter());
-                        nlf.setTypes(nlf.getTypes() & ~neverBridge);
-                        mRequestedNotificationListeners.put(listener, nlf);
+                    // also check the types they never want bridged
+                    if (si.metaData.containsKey(META_DATA_DISABLED_FILTER_TYPES)) {
+                        int neverBridge = getTypesFromStringList(si.metaData.get(
+                                META_DATA_DISABLED_FILTER_TYPES).toString());
+                        if (neverBridge != 0) {
+                            NotificationListenerFilter nlf =
+                                    mRequestedNotificationListeners.getOrDefault(
+                                            listener, new NotificationListenerFilter());
+                            nlf.setTypes(nlf.getTypes() & ~neverBridge);
+                            mRequestedNotificationListeners.put(listener, nlf);
+                        }
                     }
                 }
             }
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/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 5015985..7198de2 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -353,11 +353,10 @@
             PackageInfoLite pkgLite,
             PackageVerificationState verificationState) {
 
-        // TODO: http://b/22976637
-        // Apps installed for "all" users use the device owner to verify the app
+        // Apps installed for "all" users use the current user to verify the app
         UserHandle verifierUser = getUser();
         if (verifierUser == UserHandle.ALL) {
-            verifierUser = UserHandle.SYSTEM;
+            verifierUser = UserHandle.of(mPm.mUserManager.getCurrentUserId());
         }
         final int verifierUserId = verifierUser.getIdentifier();
 
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/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/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a757d90..f71f3b1 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -397,9 +397,21 @@
 
         /** Returns {@code true} if the incoming activity can belong to this transition. */
         boolean canCoalesce(ActivityRecord r) {
-            return mLastLaunchedActivity.mDisplayContent == r.mDisplayContent
-                    && mLastLaunchedActivity.getTask().getBounds().equals(r.getTask().getBounds())
-                    && mLastLaunchedActivity.getWindowingMode() == r.getWindowingMode();
+            if (mLastLaunchedActivity.mDisplayContent != r.mDisplayContent
+                    || mLastLaunchedActivity.getWindowingMode() != r.getWindowingMode()) {
+                return false;
+            }
+            // The current task should be non-null because it is just launched. While the
+            // last task can be cleared when starting activity with FLAG_ACTIVITY_CLEAR_TASK.
+            final Task lastTask = mLastLaunchedActivity.getTask();
+            final Task currentTask = r.getTask();
+            if (lastTask != null && currentTask != null) {
+                if (lastTask == currentTask) {
+                    return true;
+                }
+                return lastTask.getBounds().equals(currentTask.getBounds());
+            }
+            return mLastLaunchedActivity.isUid(r.launchedFromUid);
         }
 
         /** @return {@code true} if the activity matches a launched activity in this transition. */
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f5cb613..c6a2e0e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2821,6 +2821,27 @@
         }
     }
 
+    @Override
+    void waitForSyncTransactionCommit(ArraySet<WindowContainer> wcAwaitingCommit) {
+        super.waitForSyncTransactionCommit(wcAwaitingCommit);
+        if (mStartingData != null) {
+            mStartingData.mWaitForSyncTransactionCommit = true;
+        }
+    }
+
+    @Override
+    void onSyncTransactionCommitted(SurfaceControl.Transaction t) {
+        super.onSyncTransactionCommitted(t);
+        if (mStartingData == null) {
+            return;
+        }
+        mStartingData.mWaitForSyncTransactionCommit = false;
+        if (mStartingData.mRemoveAfterTransaction) {
+            mStartingData.mRemoveAfterTransaction = false;
+            removeStartingWindowAnimation(mStartingData.mPrepareRemoveAnimation);
+        }
+    }
+
     void removeStartingWindowAnimation(boolean prepareAnimation) {
         mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
         if (task != null) {
@@ -2843,6 +2864,12 @@
         final WindowState startingWindow = mStartingWindow;
         final boolean animate;
         if (mStartingData != null) {
+            if (mStartingData.mWaitForSyncTransactionCommit
+                    || mTransitionController.inCollectingTransition(startingWindow)) {
+                mStartingData.mRemoveAfterTransaction = true;
+                mStartingData.mPrepareRemoveAnimation = prepareAnimation;
+                return;
+            }
             animate = prepareAnimation && mStartingData.needRevealAnimation()
                     && mStartingWindow.isVisibleByPolicy();
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
@@ -2863,18 +2890,7 @@
                     this);
             return;
         }
-
-        if (animate && mTransitionController.inCollectingTransition(startingWindow)) {
-            // Defer remove starting window after transition start.
-            // The surface of app window could really show after the transition finish.
-            startingWindow.mSyncTransaction.addTransactionCommittedListener(Runnable::run, () -> {
-                synchronized (mAtmService.mGlobalLock) {
-                    surface.remove(true);
-                }
-            });
-        } else {
-            surface.remove(animate);
-        }
+        surface.remove(animate);
     }
 
     /**
@@ -5315,6 +5331,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/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 5f56af7..1208b6ef 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -99,13 +99,15 @@
     }
 
     public void forEachConnection(Consumer<T> consumer) {
+        final ArraySet<T> connections;
         synchronized (mActivity) {
             if (mConnections == null || mConnections.isEmpty()) {
                 return;
             }
-            for (int i = mConnections.size() - 1; i >= 0; i--) {
-                consumer.accept(mConnections.valueAt(i));
-            }
+            connections = new ArraySet<>(mConnections);
+        }
+        for (int i = connections.size() - 1; i >= 0; i--) {
+            consumer.accept(connections.valueAt(i));
         }
     }
 
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/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c5e75fa..a27f3e4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2926,8 +2926,7 @@
         // If the matching task is already in the adjacent task of the launch target. Adjust to use
         // the adjacent task as its launch target. So the existing task will be launched into the
         // closer one and won't be reparent redundantly.
-        final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null
-                ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null;
+        final Task adjacentTargetTask = mTargetRootTask.getAdjacentTask();
         if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) {
             mTargetRootTask = adjacentTargetTask;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 431f82a..f93afe8 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(
@@ -2011,7 +2008,8 @@
             return;
         }
 
-        if (r.isState(RESUMED) && r == mRootWindowContainer.getTopResumedActivity()) {
+        if ((touchedActivity == null || r == touchedActivity) && r.isState(RESUMED)
+                && r == mRootWindowContainer.getTopResumedActivity()) {
             setLastResumedActivityUncheckLocked(r, "setFocusedTask-alreadyTop");
             return;
         }
@@ -5356,7 +5354,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;
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 597c8bf..805bff2 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -1030,12 +1030,11 @@
                     canPromote = false;
                 }
 
-                // If the current window container is task and it have adjacent task, it means
-                // both tasks will open or close app toghther but we want get their opening or
-                // closing animation target independently so do not promote.
+                // If the current window container is a task with adjacent task set, the both
+                // adjacent tasks will be opened or closed together. To get their opening or
+                // closing animation target independently, skip promoting their animation targets.
                 if (current.asTask() != null
-                        && current.asTask().getAdjacentTaskFragment() != null
-                        && current.asTask().getAdjacentTaskFragment().asTask() != null) {
+                        && current.asTask().getAdjacentTask() != null) {
                     canPromote = false;
                 }
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 11d84ff..0c196d7 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -227,6 +227,7 @@
                     backType = BackNavigationInfo.TYPE_CALLBACK;
                 }
                 infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
+                infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
                 mNavigationMonitor.startMonitor(window, navigationObserver);
             }
 
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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c2bc459..bad64d3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -656,6 +656,14 @@
      */
     private InputTarget mLastImeInputTarget;
 
+
+    /**
+     * Tracks the windowToken of the input method input target and the corresponding
+     * {@link WindowContainerListener} for monitoring changes (e.g. the requested visibility
+     * change).
+     */
+    private @Nullable Pair<IBinder, WindowContainerListener> mImeTargetTokenListenerPair;
+
     /**
      * This controls the visibility and animation of the input method window.
      */
@@ -4267,7 +4275,38 @@
 
     @VisibleForTesting
     void setImeInputTarget(InputTarget target) {
+        if (mImeTargetTokenListenerPair != null) {
+            // Unregister the listener before changing to the new IME input target.
+            final WindowToken oldToken = mTokenMap.get(mImeTargetTokenListenerPair.first);
+            if (oldToken != null) {
+                oldToken.unregisterWindowContainerListener(mImeTargetTokenListenerPair.second);
+            }
+            mImeTargetTokenListenerPair = null;
+        }
         mImeInputTarget = target;
+        // Notify listeners about IME input target window visibility by the target change.
+        if (target != null) {
+            // TODO(b/276743705): Let InputTarget register the visibility change of the hierarchy.
+            final WindowState targetWin = target.getWindowState();
+            if (targetWin != null) {
+                mImeTargetTokenListenerPair = new Pair<>(targetWin.mToken.token,
+                        new WindowContainerListener() {
+                            @Override
+                            public void onVisibleRequestedChanged(boolean isVisibleRequested) {
+                                // Notify listeners for IME input target window visibility change
+                                // requested by the parent container.
+                                mWmService.dispatchImeInputTargetVisibilityChanged(
+                                        targetWin.mClient.asBinder(), isVisibleRequested,
+                                        targetWin.mActivityRecord != null
+                                                && targetWin.mActivityRecord.finishing);
+                            }
+                        });
+                targetWin.mToken.registerWindowContainerListener(
+                        mImeTargetTokenListenerPair.second);
+                mWmService.dispatchImeInputTargetVisibilityChanged(targetWin.mClient.asBinder(),
+                        targetWin.isVisible() /* visible */, false /* removed */);
+            }
+        }
         if (refreshImeSecureFlag(getPendingTransaction())) {
             mWmService.requestTraversal();
         }
@@ -4433,6 +4472,10 @@
     }
 
     private void attachImeScreenshotOnTarget(WindowState imeTarget) {
+        attachImeScreenshotOnTarget(imeTarget, false);
+    }
+
+    private void attachImeScreenshotOnTarget(WindowState imeTarget, boolean hideImeWindow) {
         final SurfaceControl.Transaction t = getPendingTransaction();
         // Remove the obsoleted IME snapshot first in case the new snapshot happens to
         // override the current one before the transition finish and the surface never be
@@ -4441,6 +4484,11 @@
         mImeScreenshot = new ImeScreenshot(
                 mWmService.mSurfaceControlFactory.apply(null), imeTarget);
         mImeScreenshot.attachAndShow(t);
+        if (mInputMethodWindow != null && hideImeWindow) {
+            // Hide the IME window when deciding to show IME snapshot on demand.
+            // InsetsController will make IME visible again before animating it.
+            mInputMethodWindow.hide(false, false);
+        }
     }
 
     /**
@@ -4458,7 +4506,7 @@
      */
     @VisibleForTesting
     void showImeScreenshot(WindowState imeTarget) {
-        attachImeScreenshotOnTarget(imeTarget);
+        attachImeScreenshotOnTarget(imeTarget, true /* hideImeWindow */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 339b6ec..747819e9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.view.Display.TYPE_INTERNAL;
 import static android.view.InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE;
 import static android.view.InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS;
@@ -2208,16 +2207,15 @@
 
     private int updateSystemBarsLw(WindowState win, int disableFlags) {
         final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final boolean multiWindowTaskVisible =
+        final boolean adjacentTasksVisible =
                 defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
-                        && task.getTopLeafTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW)
+                        && task.getTopLeafTask().getAdjacentTask() != null)
                         != null;
         final boolean freeformRootTaskVisible =
                 defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
 
-        // We need to force showing system bars when the multi-window or freeform root task is
-        // visible.
-        mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
+        // We need to force showing system bars when adjacent tasks or freeform roots visible.
+        mForceShowSystemBars = adjacentTasksVisible || freeformRootTaskVisible;
         // We need to force the consumption of the system bars if they are force shown or if they
         // are controlled by a remote insets controller.
         mForceConsumeSystemBars = mForceShowSystemBars
@@ -2238,7 +2236,7 @@
 
         int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
         appearance = configureStatusBarOpacity(appearance);
-        appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible,
+        appearance = configureNavBarOpacity(appearance, adjacentTasksVisible,
                 freeformRootTaskVisible);
 
         // Show immersive mode confirmation if needed.
diff --git a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java
new file mode 100644
index 0000000..8bc445b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java
@@ -0,0 +1,58 @@
+/*
+ * 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.server.wm;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+
+/**
+ * Callback the IME targeting window visibility change state for
+ * {@link com.android.server.inputmethod.InputMethodManagerService} to manage the IME surface
+ * visibility and z-ordering.
+ */
+public interface ImeTargetChangeListener {
+    /**
+     * Called when a non-IME-focusable overlay window being the IME layering target (e.g. a
+     * window with {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} and
+     * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flags)
+     * has changed its window visibility.
+     *
+     * @param overlayWindowToken the window token of the overlay window.
+     * @param visible            the visibility of the overlay window, {@code true} means visible
+     *                           and {@code false} otherwise.
+     * @param removed            Whether the IME target overlay window has being removed.
+     */
+    default void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken,
+            boolean visible, boolean removed) {
+    }
+
+    /**
+     * Called when the visibility of IME input target window has changed.
+     *
+     * @param imeInputTarget   the window token of the IME input target window.
+     * @param visible          the new window visibility made by {@param imeInputTarget}. visible is
+     *                         {@code true} when switching to the new visible IME input target
+     *                         window and started input, or the same input target relayout to
+     *                         visible from invisible. In contrast, visible is {@code false} when
+     *                         closing the input target, or the same input target relayout to
+     *                         invisible from visible.
+     * @param removed          Whether the IME input target window has being removed.
+     */
+    default void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget, boolean visible,
+            boolean removed) {
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
index 71dd917..1d9f24c 100644
--- a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
+++ b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
@@ -19,6 +19,7 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IBinder;
 import android.view.WindowManager;
@@ -36,16 +37,15 @@
      * @param displayId A unique id to identify the display.
      * @return {@code true} if success, {@code false} otherwise.
      */
-    public abstract boolean showImeScreenShot(IBinder imeTarget, int displayId);
+    public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId);
 
     /**
-     * Updates the IME parent for target window.
+     * Removes the IME screenshot on the given display.
      *
-     * @param imeTarget The target window to update the IME parent.
-     * @param displayId A unique id to identify the display.
+     * @param displayId The target display of showing IME screenshot.
      * @return {@code true} if success, {@code false} otherwise.
      */
-    public abstract boolean updateImeParent(IBinder imeTarget, int displayId);
+    public abstract boolean removeImeScreenshot(int displayId);
 
     /**
      * Called when {@link DisplayContent#computeImeParent()} to check if it's valid to keep
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 300a894..cff86ad 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -41,6 +41,26 @@
     /** Whether the starting window is drawn. */
     boolean mIsDisplayed;
 
+    /**
+     * For Shell transition.
+     * There will be a transition happen on attached activity, do not remove starting window during
+     * this period, because the transaction to show app window may not apply before remove starting
+     * window.
+     * Note this isn't equal to transition playing, the period should be
+     * Sync finishNow -> Start transaction apply.
+     */
+    boolean mWaitForSyncTransactionCommit;
+
+    /**
+     * For Shell transition.
+     * This starting window should be removed after applying the start transaction of transition,
+     * which ensures the app window has shown.
+     */
+    boolean mRemoveAfterTransaction;
+
+    /** Whether to prepare the removal animation. */
+    boolean mPrepareRemoveAnimation;
+
     protected StartingData(WindowManagerService service, int typeParams) {
         mService = service;
         mTypeParams = typeParams;
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/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9363eb5..5c33e64 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2362,6 +2362,22 @@
         return parentTask == null ? null : parentTask.getCreatedByOrganizerTask();
     }
 
+    /** @return the first adjacent task of this task or its parent. */
+    @Nullable
+    Task getAdjacentTask() {
+        final TaskFragment adjacentTaskFragment = getAdjacentTaskFragment();
+        if (adjacentTaskFragment != null && adjacentTaskFragment.asTask() != null) {
+            return adjacentTaskFragment.asTask();
+        }
+
+        final WindowContainer parent = getParent();
+        if (parent == null || parent.asTask() == null) {
+            return null;
+        }
+
+        return parent.asTask().getAdjacentTask();
+    }
+
     // TODO(task-merge): Figure out what's the right thing to do for places that used it.
     boolean isRootTask() {
         return getRootTask() == this;
@@ -2747,7 +2763,7 @@
             Rect outSurfaceInsets) {
         // If this task has its adjacent task, it means they should animate together. Use display
         // bounds for them could move same as full screen task.
-        if (getAdjacentTaskFragment() != null && getAdjacentTaskFragment().asTask() != null) {
+        if (getAdjacentTask() != null) {
             super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
             return;
         }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index b0a879e..e80cbb3 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1081,12 +1081,12 @@
             if (sourceTask != null && sourceTask == candidateTask) {
                 // Do nothing when task that is getting opened is same as the source.
             } else if (sourceTask != null
-                    && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
+                    && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null
                     && (sourceTask == mLaunchAdjacentFlagRootTask
                     || sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
                 // If the adjacent launch is coming from the same root, launch to
                 // adjacent root instead.
-                return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
+                return mLaunchAdjacentFlagRootTask.getAdjacentTask();
             } else {
                 return mLaunchAdjacentFlagRootTask;
             }
@@ -1095,10 +1095,8 @@
         for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
             if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
                 final Task launchRootTask = mLaunchRootTasks.get(i).task;
-                final TaskFragment adjacentTaskFragment = launchRootTask != null
-                        ? launchRootTask.getAdjacentTaskFragment() : null;
-                final Task adjacentRootTask =
-                        adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
+                final Task adjacentRootTask = launchRootTask != null
+                        ? launchRootTask.getAdjacentTask() : null;
                 if (sourceTask != null && adjacentRootTask != null
                         && (sourceTask == adjacentRootTask
                         || sourceTask.isDescendantOf(adjacentRootTask))) {
@@ -1116,16 +1114,14 @@
                 // A pinned task relaunching should be handled by its task organizer. Skip fallback
                 // launch target of a pinned task from source task.
                 || candidateTask.getWindowingMode() != WINDOWING_MODE_PINNED)) {
-            Task launchTarget = sourceTask.getCreatedByOrganizerTask();
-            if (launchTarget != null && launchTarget.getAdjacentTaskFragment() != null) {
-                if (candidateTask != null) {
-                    final Task candidateRoot = candidateTask.getCreatedByOrganizerTask();
-                    if (candidateRoot != null && candidateRoot != launchTarget
-                            && launchTarget == candidateRoot.getAdjacentTaskFragment()) {
-                        launchTarget = candidateRoot;
-                    }
+            final Task adjacentTarget = sourceTask.getAdjacentTask();
+            if (adjacentTarget != null) {
+                if (candidateTask != null
+                        && (candidateTask == adjacentTarget
+                        || candidateTask.isDescendantOf(adjacentTarget))) {
+                    return adjacentTarget;
                 }
-                return launchTarget;
+                return sourceTask.getCreatedByOrganizerTask();
             }
         }
 
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/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 184293e..5626aa7 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -681,6 +681,7 @@
         final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
         removalInfo.taskId = task.mTaskId;
         removalInfo.playRevealAnimation = prepareAnimation
+                && task.getDisplayContent() != null
                 && task.getDisplayInfo().state == Display.STATE_ON;
         final boolean playShiftUpAnimation = !task.inMultiWindowMode();
         final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 969afe5..4922523 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -848,6 +848,16 @@
     }
 
     /**
+     * Sets by the {@link com.android.server.inputmethod.InputMethodManagerService} to monitor
+     * the visibility change of the IME targeted windows.
+     *
+     * @see ImeTargetChangeListener#onImeTargetOverlayVisibilityChanged
+     * @see ImeTargetChangeListener#onImeInputTargetVisibilityChanged
+     */
+    public abstract void setInputMethodTargetChangeListener(
+            @NonNull ImeTargetChangeListener listener);
+
+    /**
      * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
      */
     public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 99d0ea8..62b3c7c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -723,6 +723,9 @@
 
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
+
+    @Nullable ImeTargetChangeListener mImeTargetChangeListener;
+
     SettingsObserver mSettingsObserver;
     final EmbeddedWindowController mEmbeddedWindowController;
     final AnrController mAnrController;
@@ -1807,6 +1810,10 @@
 
             if (imMayMove) {
                 displayContent.computeImeTarget(true /* updateImeTarget */);
+                if (win.isImeOverlayLayeringTarget()) {
+                    dispatchImeTargetOverlayVisibilityChanged(client.asBinder(),
+                            win.isVisibleRequestedOrAdding(), false /* removed */);
+                }
             }
 
             // Don't do layout here, the window must call
@@ -2328,6 +2335,8 @@
                 winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
             }
 
+            final boolean wasVisible = win.isVisible();
+
             win.mRelayoutCalled = true;
             win.mInRelayout = true;
 
@@ -2336,7 +2345,6 @@
                     "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
                             viewVisibility, new RuntimeException().fillInStackTrace());
 
-
             win.setDisplayLayoutNeeded();
             win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
 
@@ -2501,6 +2509,18 @@
             }
             win.mInRelayout = false;
 
+            final boolean winVisibleChanged = win.isVisible() != wasVisible;
+            if (win.isImeOverlayLayeringTarget() && winVisibleChanged) {
+                dispatchImeTargetOverlayVisibilityChanged(client.asBinder(),
+                        win.isVisible(), false /* removed */);
+            }
+            // Notify listeners about IME input target window visibility change.
+            final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win;
+            if (isImeInputTarget && winVisibleChanged) {
+                dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(),
+                        win.isVisible() /* visible */, false /* removed */);
+            }
+
             if (outSyncIdBundle != null) {
                 final int maybeSyncSeqId;
                 if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
@@ -3325,6 +3345,30 @@
         });
     }
 
+    void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, boolean visible,
+            boolean removed) {
+        if (mImeTargetChangeListener != null) {
+            if (DEBUG_INPUT_METHOD) {
+                Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token)
+                        + "visible=" + visible + ", removed=" + removed);
+            }
+            mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token,
+                    visible, removed));
+        }
+    }
+
+    void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible,
+            boolean removed) {
+        if (mImeTargetChangeListener != null) {
+            if (DEBUG_INPUT_METHOD) {
+                Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token)
+                        + "visible=" + visible + ", removed=" + removed);
+            }
+            mH.post(() -> mImeTargetChangeListener.onImeInputTargetVisibilityChanged(token,
+                    visible, removed));
+        }
+    }
+
     @Override
     public void setSwitchingUser(boolean switching) {
         if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -8262,13 +8306,19 @@
             }
             return null;
         }
+
+        @Override
+        public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
+            synchronized (mGlobalLock) {
+                mImeTargetChangeListener = listener;
+            }
+        }
     }
 
     private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
 
-        // TODO(b/258048231): Track IME visibility change in bugreport when invocations.
         @Override
-        public boolean showImeScreenShot(@NonNull IBinder imeTarget, int displayId) {
+        public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
             synchronized (mGlobalLock) {
                 final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
                 if (imeTargetWindow == null) {
@@ -8284,24 +8334,18 @@
                 return true;
             }
         }
-
-        // TODO(b/258048231): Track IME visibility change in bugreport when invocations.
         @Override
-        public boolean updateImeParent(@NonNull IBinder imeTarget, int displayId) {
+        public boolean removeImeScreenshot(int displayId) {
             synchronized (mGlobalLock) {
-                final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
-                if (imeTargetWindow == null) {
-                    return false;
-                }
                 final DisplayContent dc = mRoot.getDisplayContent(displayId);
                 if (dc == null) {
-                    Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to update ime parent");
+                    Slog.w(TAG, "Invalid displayId:" + displayId
+                            + ", fail to remove ime screenshot");
                     return false;
                 }
-
-                dc.updateImeParent();
-                return true;
+                dc.removeImeSurfaceImmediately();
             }
+            return true;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d1618e9..a299592 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -166,6 +166,7 @@
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
 import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
+import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
 import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.REMOVED;
 import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
@@ -353,6 +354,7 @@
     // overlay window is hidden because the owning app is suspended
     private boolean mHiddenWhileSuspended;
     private boolean mAppOpVisibility = true;
+
     boolean mPermanentlyHidden; // the window should never be shown again
     // This is a non-system overlay window that is currently force hidden.
     private boolean mForceHideNonSystemOverlayWindow;
@@ -2349,6 +2351,10 @@
         }
         super.removeImmediately();
 
+        if (isImeOverlayLayeringTarget()) {
+            mWmService.dispatchImeTargetOverlayVisibilityChanged(mClient.asBinder(),
+                    false /* visible */, true /* removed */);
+        }
         final DisplayContent dc = getDisplayContent();
         if (isImeLayeringTarget()) {
             // Remove the attached IME screenshot surface.
@@ -2359,6 +2365,8 @@
             dc.computeImeTarget(true /* updateImeTarget */);
         }
         if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) {
+            mWmService.dispatchImeInputTargetVisibilityChanged(mClient.asBinder(),
+                    false /* visible */, true /* removed */);
             dc.updateImeInputAndControlTarget(null);
         }
 
@@ -4027,6 +4035,11 @@
         for (Rect r : mUnrestrictedKeepClearAreas) {
             r.dumpDebug(proto, UNRESTRICTED_KEEP_CLEAR_AREAS);
         }
+        if (mMergedLocalInsetsSources != null) {
+            for (int i = 0; i < mMergedLocalInsetsSources.size(); ++i) {
+                mMergedLocalInsetsSources.valueAt(i).dumpDebug(proto, MERGED_LOCAL_INSETS_SOURCES);
+            }
+        }
         proto.end(token);
     }
 
@@ -5493,6 +5506,14 @@
         return getDisplayContent().getImeTarget(IME_TARGET_LAYERING) == this;
     }
 
+    /**
+     * Whether the window is non-focusable IME overlay layering target.
+     */
+    boolean isImeOverlayLayeringTarget() {
+        return isImeLayeringTarget()
+                && (mAttrs.flags & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
+    }
+
     WindowState getImeLayeringTarget() {
         final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING);
         return target != null ? target.getWindow() : null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
index ee73f8a..82f9aad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
@@ -17,50 +17,28 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.admin.BundlePolicyValue;
 import android.app.admin.PackagePolicyKey;
 import android.app.admin.PolicyKey;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Parcelable;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.Xml;
+import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
-import libcore.io.IoUtils;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Objects;
 
-// TODO(b/266704763): clean this up and stop creating separate files for each value, the code here
-//  is copied from UserManagerService, however it doesn't currently handle setting different
-//  restrictions for the same package in different users, it also will not remove the files for
-//  outdated restrictions, this will all get fixed when we save it as part of the policies file
-//  rather than in its own files.
 final class BundlePolicySerializer extends PolicySerializer<Bundle> {
 
     private static final String TAG = "BundlePolicySerializer";
 
-    private static final String ATTR_FILE_NAME = "file-name";
-
-    private static final String RESTRICTIONS_FILE_PREFIX = "AppRestrictions_";
-    private static final String XML_SUFFIX = ".xml";
-
-    private static final String TAG_RESTRICTIONS = "restrictions";
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
     private static final String ATTR_KEY = "key";
@@ -83,62 +61,26 @@
             throw new IllegalArgumentException("policyKey is not of type "
                     + "PackagePolicyKey");
         }
-        String packageName = ((PackagePolicyKey) policyKey).getPackageName();
-        String fileName = packageToRestrictionsFileName(packageName, value);
-        writeApplicationRestrictionsLAr(fileName, value);
-        serializer.attribute(/* namespace= */ null, ATTR_FILE_NAME, fileName);
+        writeBundle(value, serializer);
     }
 
-    @Nullable
     @Override
     BundlePolicyValue readFromXml(TypedXmlPullParser parser) {
-        String fileName = parser.getAttributeValue(/* namespace= */ null, ATTR_FILE_NAME);
-
-        return new BundlePolicyValue(readApplicationRestrictions(fileName));
-    }
-
-    private static String packageToRestrictionsFileName(String packageName, Bundle restrictions) {
-        return RESTRICTIONS_FILE_PREFIX + packageName + Objects.hash(restrictions) + XML_SUFFIX;
-    }
-
-    @GuardedBy("mAppRestrictionsLock")
-    private static Bundle readApplicationRestrictions(String fileName) {
-        AtomicFile restrictionsFile =
-                new AtomicFile(new File(Environment.getDataSystemDirectory(), fileName));
-        return readApplicationRestrictions(restrictionsFile);
-    }
-
-    @VisibleForTesting
-    @GuardedBy("mAppRestrictionsLock")
-    static Bundle readApplicationRestrictions(AtomicFile restrictionsFile) {
-        final Bundle restrictions = new Bundle();
-        final ArrayList<String> values = new ArrayList<>();
-        if (!restrictionsFile.getBaseFile().exists()) {
-            return restrictions;
-        }
-
-        FileInputStream fis = null;
+        Bundle bundle = new Bundle();
+        ArrayList<String> values = new ArrayList<>();
         try {
-            fis = restrictionsFile.openRead();
-            final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
-            XmlUtils.nextElement(parser);
-            if (parser.getEventType() != XmlPullParser.START_TAG) {
-                Slog.e(TAG, "Unable to read restrictions file "
-                        + restrictionsFile.getBaseFile());
-                return restrictions;
+            final int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                readBundle(bundle, values, parser);
             }
-            while (parser.next() != XmlPullParser.END_DOCUMENT) {
-                readEntry(restrictions, values, parser);
-            }
-        } catch (IOException | XmlPullParserException e) {
-            Slog.w(TAG, "Error parsing " + restrictionsFile.getBaseFile(), e);
-        } finally {
-            IoUtils.closeQuietly(fis);
+        } catch (XmlPullParserException | IOException e) {
+            Log.e(TAG, "Error parsing Bundle policy.", e);
+            return null;
         }
-        return restrictions;
+        return new BundlePolicyValue(bundle);
     }
 
-    private static void readEntry(Bundle restrictions, ArrayList<String> values,
+    private static void readBundle(Bundle restrictions, ArrayList<String> values,
             TypedXmlPullParser parser) throws XmlPullParserException, IOException {
         int type = parser.getEventType();
         if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
@@ -186,37 +128,11 @@
         Bundle childBundle = new Bundle();
         int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-            readEntry(childBundle, values, parser);
+            readBundle(childBundle, values, parser);
         }
         return childBundle;
     }
 
-    private static void writeApplicationRestrictionsLAr(String fileName, Bundle restrictions) {
-        AtomicFile restrictionsFile = new AtomicFile(
-                new File(Environment.getDataSystemDirectory(), fileName));
-        writeApplicationRestrictionsLAr(restrictions, restrictionsFile);
-    }
-
-    static void writeApplicationRestrictionsLAr(Bundle restrictions, AtomicFile restrictionsFile) {
-        FileOutputStream fos = null;
-        try {
-            fos = restrictionsFile.startWrite();
-            final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
-            serializer.startTag(null, TAG_RESTRICTIONS);
-            writeBundle(restrictions, serializer);
-            serializer.endTag(null, TAG_RESTRICTIONS);
-
-            serializer.endDocument();
-            restrictionsFile.finishWrite(fos);
-        } catch (Exception e) {
-            restrictionsFile.failWrite(fos);
-            Slog.e(TAG, "Error writing application restrictions list", e);
-        }
-    }
-
     private static void writeBundle(Bundle restrictions, TypedXmlSerializer serializer)
             throws IOException {
         for (String key : restrictions.keySet()) {
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..7e5d5aa 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,8 +11052,7 @@
             return false;
         }
 
-        if (!isPermissionCheckFlagEnabled()) {
-            // TODO: Figure out if something like this needs to be restored for policy engine
+        if (!isPermissionCheckFlagEnabled() && !isPolicyEngineForFinanceFlagEnabled()) {
             final ComponentName profileOwner = getProfileOwnerAsUser(userId);
             if (profileOwner == null) {
                 return false;
@@ -11009,17 +11066,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 +11093,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);
     }
@@ -11583,7 +11639,7 @@
                         caller.getUserId());
             }
             setBackwardsCompatibleAppRestrictions(
-                    packageName, restrictions, caller.getUserHandle());
+                    caller, packageName, restrictions, caller.getUserHandle());
         } else {
             Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                     && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
@@ -11604,17 +11660,28 @@
     }
 
     /**
-     * Set app restrictions in user manager to keep backwards compatibility for the old
-     * getApplicationRestrictions API.
+     * Set app restrictions in user manager for DPC callers only to keep backwards compatibility
+     * for the old getApplicationRestrictions API.
      */
     private void setBackwardsCompatibleAppRestrictions(
-            String packageName, Bundle restrictions, UserHandle userHandle) {
-        Bundle restrictionsToApply = restrictions == null || restrictions.isEmpty()
-                ? getAppRestrictionsSetByAnyAdmin(packageName, userHandle)
-                : restrictions;
-        mInjector.binderWithCleanCallingIdentity(() -> {
-            mUserManager.setApplicationRestrictions(packageName, restrictionsToApply, userHandle);
-        });
+            CallerIdentity caller, String packageName, Bundle restrictions, UserHandle userHandle) {
+        if ((caller.hasAdminComponent() && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))) {
+            Bundle restrictionsToApply = restrictions == null || restrictions.isEmpty()
+                    ? getAppRestrictionsSetByAnyAdmin(packageName, userHandle)
+                    : restrictions;
+            mInjector.binderWithCleanCallingIdentity(() -> {
+                mUserManager.setApplicationRestrictions(packageName, restrictionsToApply,
+                        userHandle);
+            });
+        } else {
+            // Notify package of changes via an intent - only sent to explicitly registered
+            // receivers. Sending here because For DPCs, this is being sent in UMS.
+            final Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+            changeIntent.setPackage(packageName);
+            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            mContext.sendBroadcastAsUser(changeIntent, userHandle);
+        }
     }
 
     private Bundle getAppRestrictionsSetByAnyAdmin(String packageName, UserHandle userHandle) {
@@ -13978,16 +14045,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 +14083,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 +14107,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 +14739,7 @@
         if (isPolicyEngineForFinanceFlagEnabled()) {
             EnforcingAdmin enforcingAdmin;
             synchronized (getLockObject()) {
-                enforcingAdmin = enforceCanCallLockTaskLocked(who, callerPackageName);
+                enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
             }
             if (packages.length == 0) {
                 mDevicePolicyEngine.removeLocalPolicy(
@@ -14764,8 +14866,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 +14943,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 +18407,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 +18464,7 @@
                 }
             }
 
-            boolean compatible = !hasIncompatibleAccounts(am, accounts);
+            boolean compatible = !hasIncompatibleAccounts(userId);
             if (compatible) {
                 Slogf.w(LOG_TAG, "All accounts are compatible");
             } else {
@@ -18352,37 +18474,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 +22366,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 +22606,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 +22779,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 +22806,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 +22833,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 +22859,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 +22876,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 +23256,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 +23328,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 +23552,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/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 7d4f87d..a6ada4d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -38,6 +38,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.view.Display;
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -144,6 +145,26 @@
         }
     }
 
+    @Test
+    public void testShowImeScreenshot() {
+        synchronized (ImfLock.class) {
+            mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY,
+                    null /* statsToken */);
+        }
+
+        verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken),
+                eq(Display.DEFAULT_DISPLAY));
+    }
+
+    @Test
+    public void testRemoveImeScreenshot() {
+        synchronized (ImfLock.class) {
+            mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY);
+        }
+
+        verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
+    }
+
     private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
         return mInputMethodManagerService.startInputOrWindowGainedFocus(
                 StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 2a256f2..3871e1d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -24,7 +24,15 @@
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
 import static com.android.server.inputmethod.InputMethodManagerService.FALLBACK_DISPLAY_ID;
 import static com.android.server.inputmethod.InputMethodManagerService.ImeDisplayValidator;
 
@@ -37,11 +45,13 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.server.wm.ImeTargetChangeListener;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 /**
  * Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when
@@ -53,6 +63,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTestBase {
     private ImeVisibilityStateComputer mComputer;
+    private ImeTargetChangeListener mListener;
     private int mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL;
 
     @Before
@@ -69,7 +80,11 @@
                 return displayId -> mImeDisplayPolicy;
             }
         };
+        ArgumentCaptor<ImeTargetChangeListener> captor = ArgumentCaptor.forClass(
+                ImeTargetChangeListener.class);
+        verify(mMockWindowManagerInternal).setInputMethodTargetChangeListener(captor.capture());
         mComputer = new ImeVisibilityStateComputer(mInputMethodManagerService, injector);
+        mListener = captor.getValue();
     }
 
     @Test
@@ -196,6 +211,53 @@
                 lastState.isRequestedImeVisible());
     }
 
+    @Test
+    public void testOnInteractiveChanged() {
+        mComputer.getOrCreateWindowState(mWindowToken);
+        // Precondition: ensure IME has shown before hiding request.
+        mComputer.requestImeVisibility(mWindowToken, true);
+        mComputer.setInputShown(true);
+
+        // No need any visibility change When initially shows IME on the device was interactive.
+        ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged(
+                mWindowToken, true /* interactive */);
+        assertThat(result).isNull();
+
+        // Show the IME screenshot to capture the last IME visible state when the device inactive.
+        result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */);
+        assertThat(result).isNotNull();
+        assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT);
+        assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS);
+
+        // Remove the IME screenshot when the device became interactive again.
+        result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */);
+        assertThat(result).isNotNull();
+        assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT);
+        assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS);
+    }
+
+    @Test
+    public void testOnApplyImeVisibilityFromComputer() {
+        final IBinder testImeTargetOverlay = new Binder();
+        final IBinder testImeInputTarget = new Binder();
+
+        // Simulate a test IME layering target overlay fully occluded the IME input target.
+        mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay, true, false);
+        mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, false, false);
+        final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class);
+        final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass(
+                ImeVisibilityResult.class);
+        verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(),
+                resultCaptor.capture());
+        final IBinder imeInputTarget = targetCaptor.getValue();
+        final ImeVisibilityResult result = resultCaptor.getValue();
+
+        // Verify the computer will callback hiding IME state to IMMS.
+        assertThat(imeInputTarget).isEqualTo(testImeInputTarget);
+        assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT);
+        assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE);
+    }
+
     private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
         final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
                 0, true, true, true);
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 90691a7..c80ecbf 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -62,6 +62,7 @@
 import com.android.server.SystemService;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.ImeTargetVisibilityPolicy;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
@@ -113,6 +114,7 @@
     @Mock protected IInputMethod mMockInputMethod;
     @Mock protected IBinder mMockInputMethodBinder;
     @Mock protected IInputManager mMockIInputManager;
+    @Mock protected ImeTargetVisibilityPolicy mMockImeTargetVisibilityPolicy;
 
     protected Context mContext;
     protected MockitoSession mMockingSession;
@@ -166,6 +168,8 @@
                 .when(() -> LocalServices.getService(DisplayManagerInternal.class));
         doReturn(mMockUserManagerInternal)
                 .when(() -> LocalServices.getService(UserManagerInternal.class));
+        doReturn(mMockImeTargetVisibilityPolicy)
+                .when(() -> LocalServices.getService(ImeTargetVisibilityPolicy.class));
         doReturn(mMockIInputMethodManager)
                 .when(() -> ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
         doReturn(mMockIPlatformCompat)
@@ -218,6 +222,7 @@
                         false);
         mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread,
                 mMockInputMethodBindingController);
+        spyOn(mInputMethodManagerService);
 
         // Start a InputMethodManagerService.Lifecycle to publish and manage the lifecycle of
         // InputMethodManagerService, which is closer to the real situation.
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/HighBrightnessModeMetadataMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/display/HighBrightnessModeMetadataMapperTest.java
new file mode 100644
index 0000000..d9fbba5
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/HighBrightnessModeMetadataMapperTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.server.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class HighBrightnessModeMetadataMapperTest {
+
+    private HighBrightnessModeMetadataMapper mHighBrightnessModeMetadataMapper;
+
+    @Before
+    public void setUp() {
+        mHighBrightnessModeMetadataMapper = new HighBrightnessModeMetadataMapper();
+    }
+
+    @Test
+    public void testGetHighBrightnessModeMetadata() {
+        // Display device is null
+        final LogicalDisplay display = mock(LogicalDisplay.class);
+        when(display.getPrimaryDisplayDeviceLocked()).thenReturn(null);
+        assertNull(mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display));
+
+        // No HBM metadata stored for this display yet
+        final DisplayDevice device = mock(DisplayDevice.class);
+        when(display.getPrimaryDisplayDeviceLocked()).thenReturn(device);
+        HighBrightnessModeMetadata hbmMetadata =
+                mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+        assertTrue(hbmMetadata.getHbmEventQueue().isEmpty());
+        assertTrue(hbmMetadata.getRunningStartTimeMillis() < 0);
+
+        // Modify the metadata
+        long startTimeMillis = 100;
+        long endTimeMillis = 200;
+        long setTime = 300;
+        hbmMetadata.addHbmEvent(new HbmEvent(startTimeMillis, endTimeMillis));
+        hbmMetadata.setRunningStartTimeMillis(setTime);
+        hbmMetadata =
+                mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+        assertEquals(1, hbmMetadata.getHbmEventQueue().size());
+        assertEquals(startTimeMillis,
+                hbmMetadata.getHbmEventQueue().getFirst().getStartTimeMillis());
+        assertEquals(endTimeMillis, hbmMetadata.getHbmEventQueue().getFirst().getEndTimeMillis());
+        assertEquals(setTime, hbmMetadata.getRunningStartTimeMillis());
+    }
+}
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/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/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index aca96ad..aad373f 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -445,14 +445,14 @@
                         + "    <library \n"
                         + "        name=\"foo\"\n"
                         + "        file=\"" + mFooJar + "\"\n"
-                        + "        on-bootclasspath-before=\"Q\"\n"
+                        + "        on-bootclasspath-before=\"A\"\n"
                         + "        on-bootclasspath-since=\"W\"\n"
                         + "     />\n\n"
                         + " </permissions>";
         parseSharedLibraries(contents);
         assertFooIsOnlySharedLibrary();
         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
-        assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+        assertThat(entry.onBootclasspathBefore).isEqualTo("A");
         assertThat(entry.onBootclasspathSince).isEqualTo("W");
     }
 
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/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 90d1488..4406d83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -71,10 +71,10 @@
 import android.testing.TestableContext;
 import android.util.ArraySet;
 import android.util.Pair;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
 import android.util.Xml;
 
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
 import com.google.common.collect.ImmutableList;
@@ -92,6 +92,7 @@
 import java.io.ByteArrayOutputStream;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 
 public class NotificationListenersTest extends UiServiceTestCase {
 
@@ -626,6 +627,58 @@
                 .onNotificationChannelGroupModification(anyString(), any(), any(), anyInt());
     }
 
+    @Test
+    public void testNotificationListenerFilter_threadSafety() throws Exception {
+        testThreadSafety(() -> {
+            mListeners.setNotificationListenerFilter(
+                    new Pair<>(new ComponentName("pkg1", "cls1"), 0),
+                    new NotificationListenerFilter());
+            mListeners.setNotificationListenerFilter(
+                    new Pair<>(new ComponentName("pkg2", "cls2"), 10),
+                    new NotificationListenerFilter());
+            mListeners.setNotificationListenerFilter(
+                    new Pair<>(new ComponentName("pkg3", "cls3"), 11),
+                    new NotificationListenerFilter());
+
+            mListeners.onUserRemoved(10);
+            mListeners.onPackagesChanged(true, new String[]{"pkg1", "pkg2"}, new int[]{0, 0});
+        }, 20, 50);
+    }
+
+    /**
+     * Helper method to test the thread safety of some operations.
+     *
+     * <p>Runs the supplied {@code operationToTest}, {@code nRunsPerThread} times,
+     * concurrently using {@code nThreads} threads, and waits for all of them to finish.
+     */
+    private static void testThreadSafety(Runnable operationToTest, int nThreads,
+            int nRunsPerThread) throws InterruptedException {
+        final CountDownLatch startLatch = new CountDownLatch(1);
+        final CountDownLatch doneLatch = new CountDownLatch(nThreads);
+
+        for (int i = 0; i < nThreads; i++) {
+            Runnable threadRunnable = () -> {
+                try {
+                    startLatch.await();
+                    for (int j = 0; j < nRunsPerThread; j++) {
+                        operationToTest.run();
+                    }
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    doneLatch.countDown();
+                }
+            };
+            new Thread(threadRunnable, "Test Thread #" + i).start();
+        }
+
+        // Ready set go
+        startLatch.countDown();
+
+        // Wait for all test threads to be done.
+        doneLatch.await();
+    }
+
     private ManagedServices.ManagedServiceInfo getParcelingListener(
             final NotificationChannelGroup toParcel)
             throws RemoteException {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9cfdaa7..dd9f3cb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5447,6 +5447,29 @@
     }
 
     @Test
+    public void testVisitUris_callStyle() {
+        Icon personIcon = Icon.createWithContentUri("content://media/person");
+        Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
+        Person callingPerson = new Person.Builder().setName("Someone")
+                .setIcon(personIcon)
+                .build();
+        PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
+        Notification n = new Notification.Builder(mContext, "a")
+                .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent)
+                        .setVerificationIcon(verificationIcon))
+                .setContentTitle("Calling...")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .build();
+
+        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+        n.visitUris(visitor);
+
+        verify(visitor, times(1)).accept(eq(personIcon.getUri()));
+        verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
+    }
+
+    @Test
     public void testVisitUris_audioContentsString() throws Exception {
         final Uri audioContents = Uri.parse("content://com.example/audio");
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 4e001fe..37c4b37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -28,6 +28,7 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -501,6 +502,12 @@
         onActivityLaunched(mTrampolineActivity);
         mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
                 mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
+
+        // Simulate a corner case that the trampoline activity is removed by CLEAR_TASK.
+        // The 2 launch events can still be coalesced to one by matching the uid.
+        mTrampolineActivity.takeFromHistory();
+        assertNull(mTrampolineActivity.getTask());
+
         notifyActivityLaunched(START_SUCCESS, mTopActivity);
         transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8f2b470..0033e3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2399,7 +2399,10 @@
         holder.addConnection(connection);
         assertTrue(holder.isActivityVisible());
         final int[] count = new int[1];
-        final Consumer<Object> c = conn -> count[0]++;
+        final Consumer<Object> c = conn -> {
+            count[0]++;
+            assertFalse(Thread.holdsLock(activity));
+        };
         holder.forEachConnection(c);
         assertEquals(1, count[0]);
 
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/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 17ae215..6d7f2c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -232,11 +232,36 @@
 
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
         window.setOnBackInvokedCallbackInfo(
-                new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+                new OnBackInvokedCallbackInfo(
+                        callback,
+                        OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                        /* isAnimationCallback = */ false));
 
         BackNavigationInfo backNavigationInfo = startBackNavigation();
         assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
         assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK);
+        assertThat(backNavigationInfo.isAnimationCallback()).isEqualTo(false);
+        assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
+    }
+
+    @Test
+    public void backInfoWithAnimationCallback() {
+        WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_WALLPAPER,
+                "Wallpaper");
+        addToWindowMap(window, true);
+        makeWindowVisibleAndDrawn(window);
+
+        IOnBackInvokedCallback callback = createOnBackInvokedCallback();
+        window.setOnBackInvokedCallbackInfo(
+                new OnBackInvokedCallbackInfo(
+                        callback,
+                        OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                        /* isAnimationCallback = */ true));
+
+        BackNavigationInfo backNavigationInfo = startBackNavigation();
+        assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
+        assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK);
+        assertThat(backNavigationInfo.isAnimationCallback()).isEqualTo(true);
         assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
     }
 
@@ -364,7 +389,10 @@
 
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
         window.setOnBackInvokedCallbackInfo(
-                new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+                new OnBackInvokedCallbackInfo(
+                        callback,
+                        OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                        /* isAnimationCallback = */ false));
 
         BackNavigationInfo backNavigationInfo = startBackNavigation();
         assertThat(backNavigationInfo).isNull();
@@ -450,14 +478,20 @@
     private IOnBackInvokedCallback withSystemCallback(Task task) {
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
         task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
-                new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM));
+                new OnBackInvokedCallbackInfo(
+                        callback,
+                        OnBackInvokedDispatcher.PRIORITY_SYSTEM,
+                        /* isAnimationCallback = */ false));
         return callback;
     }
 
     private IOnBackInvokedCallback withAppCallback(Task task) {
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
         task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
-                new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+                new OnBackInvokedCallbackInfo(
+                        callback,
+                        OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                        /* isAnimationCallback = */ false));
         return callback;
     }
 
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/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 2065540..a8fc25f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -84,12 +84,14 @@
     }
 
     @Test
-    public void testControlsForDispatch_multiWindowTaskVisible() {
+    public void testControlsForDispatch_adjacentTasksVisible() {
         addStatusBar();
         addNavigationBar();
 
-        final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
-                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+        final Task task1 = createTask(mDisplayContent);
+        final Task task2 = createTask(mDisplayContent);
+        task1.setAdjacentTaskFragment(task2);
+        final WindowState win = createAppWindow(task1, WINDOWING_MODE_MULTI_WINDOW, "app");
         final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
 
         // The app must not control any system bars.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 49d8da1..9d597b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -586,6 +586,15 @@
         // Making the activity0 be the focused activity and ensure the focused app is updated.
         activity0.moveFocusableActivityToTop("test");
         assertEquals(activity0, mDisplayContent.mFocusedApp);
+
+        // Moving activity1 to top and make both the two activities resumed.
+        activity1.moveFocusableActivityToTop("test");
+        activity0.setState(RESUMED, "test");
+        activity1.setState(RESUMED, "test");
+
+        // Verifies that the focus app can be updated to an Activity in the adjacent TF
+        mAtm.setFocusedTask(task.mTaskId, activity0);
+        assertEquals(activity0, mDisplayContent.mFocusedApp);
     }
 
     @Test
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 766e74c..460a603 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -20,6 +20,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.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
@@ -85,17 +86,25 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.InputConfig;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
+import android.util.MergedConfiguration;
 import android.view.Gravity;
+import android.view.IWindow;
+import android.view.IWindowSessionCallback;
 import android.view.InputWindowHandle;
 import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.window.ClientWindowFrames;
 import android.window.ITaskFragmentOrganizer;
 import android.window.TaskFragmentOrganizer;
 
@@ -1280,4 +1289,118 @@
         assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
                      new ArraySet(unrestrictedKeepClearAreas));
     }
+
+    @Test
+    public void testImeTargetChangeListener_OnImeInputTargetVisibilityChanged() {
+        final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
+        mWm.mImeTargetChangeListener = listener;
+
+        final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+                createActivityRecord(mDisplayContent), "imeTarget");
+
+        imeTarget.mActivityRecord.setVisibleRequested(true);
+        makeWindowVisible(imeTarget);
+        mDisplayContent.setImeInputTarget(imeTarget);
+        waitHandlerIdle(mWm.mH);
+
+        assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
+        assertThat(listener.mIsRemoved).isFalse();
+        assertThat(listener.mIsVisibleForImeInputTarget).isTrue();
+
+        imeTarget.mActivityRecord.setVisibleRequested(false);
+        waitHandlerIdle(mWm.mH);
+
+        assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
+        assertThat(listener.mIsRemoved).isFalse();
+        assertThat(listener.mIsVisibleForImeInputTarget).isFalse();
+
+        imeTarget.removeImmediately();
+        assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
+        assertThat(listener.mIsRemoved).isTrue();
+        assertThat(listener.mIsVisibleForImeInputTarget).isFalse();
+    }
+
+    @SetupWindows(addWindows = {W_INPUT_METHOD})
+    @Test
+    public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() {
+        final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
+        mWm.mImeTargetChangeListener = listener;
+
+        // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible.
+        final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+                mDisplayContent);
+        final IWindow client = new TestIWindow();
+        final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
+            @Override
+            public void onAnimatorScaleChanged(float v) throws RemoteException {
+
+            }
+        });
+        final ClientWindowFrames outFrames = new ClientWindowFrames();
+        final MergedConfiguration outConfig = new MergedConfiguration();
+        final SurfaceControl outSurfaceControl = new SurfaceControl();
+        final InsetsState outInsetsState = new InsetsState();
+        final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
+        final Bundle outBundle = new Bundle();
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                TYPE_APPLICATION_OVERLAY);
+        params.setTitle("imeLayeringTargetOverlay");
+        params.token = windowToken.token;
+        params.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
+
+        mWm.addWindow(session, client, params, View.VISIBLE, DEFAULT_DISPLAY,
+                0 /* userUd */, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
+                new InsetsSourceControl.Array(), new Rect(), new float[1]);
+        mWm.relayoutWindow(session, client, params, 100, 200, View.VISIBLE, 0, 0, 0,
+                outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
+        waitHandlerIdle(mWm.mH);
+
+        final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow(
+                w -> w.mClient.asBinder() == client.asBinder());
+        assertThat(imeLayeringTargetOverlay.isVisible()).isTrue();
+        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+        assertThat(listener.mIsRemoved).isFalse();
+        assertThat(listener.mIsVisibleForImeTargetOverlay).isTrue();
+
+        // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible.
+        mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0,
+                outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
+        waitHandlerIdle(mWm.mH);
+
+        assertThat(imeLayeringTargetOverlay.isVisible()).isFalse();
+        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+        assertThat(listener.mIsRemoved).isFalse();
+        assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
+
+        // Scenario 3: test removeWindow to remove the Ime layering target overlay window.
+        mWm.removeWindow(session, client);
+        waitHandlerIdle(mWm.mH);
+
+        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+        assertThat(listener.mIsRemoved).isTrue();
+        assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
+    }
+
+    private static class TestImeTargetChangeListener implements ImeTargetChangeListener {
+        private IBinder mImeTargetToken;
+        private boolean mIsRemoved;
+        private boolean mIsVisibleForImeTargetOverlay;
+        private boolean mIsVisibleForImeInputTarget;
+
+        @Override
+        public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, boolean visible,
+                boolean removed) {
+            mImeTargetToken = overlayWindowToken;
+            mIsVisibleForImeTargetOverlay = visible;
+            mIsRemoved = removed;
+        }
+
+        @Override
+        public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
+                boolean visibleRequested, boolean removed) {
+            mImeTargetToken = imeInputTarget;
+            mIsVisibleForImeInputTarget = visibleRequested;
+            mIsRemoved = removed;
+        }
+    }
 }
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/OWNERS b/telephony/OWNERS
index 025869d..3158ad8 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -11,12 +11,6 @@
 amruthr@google.com
 sasindran@google.com
 
-# Temporarily reduced the owner during refactoring
-per-file SubscriptionManager.java=set noparent
-per-file SubscriptionManager.java=jackyu@google.com,amruthr@google.com
-per-file SubscriptionInfo.java=set noparent
-per-file SubscriptionInfo.java=jackyu@google.com,amruthr@google.com
-
 # Requiring TL ownership for new carrier config keys.
 per-file CarrierConfigManager.java=set noparent
 per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 905a90c..caafce2 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -331,8 +331,8 @@
      * Same as {@link #checkCallingOrSelfReadSubscriberIdentifiers(Context, int, String, String,
      * String)} except this allows an additional parameter reportFailure. Caller may not want to
      * report a failure when this is an internal/intermediate check, for example,
-     * SubscriptionController calls this with an INVALID_SUBID to check if caller has the required
-     * permissions to bypass carrier privilege checks.
+     * SubscriptionManagerService calls this with an INVALID_SUBID to check if caller has the
+     * required permissions to bypass carrier privilege checks.
      * @param reportFailure Indicates if failure should be reported.
      */
     public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 559faf9..64e4356 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -91,8 +91,7 @@
 import java.util.stream.Collectors;
 
 /**
- * SubscriptionManager is the application interface to SubscriptionController
- * and provides information about the current Telephony Subscriptions.
+ * Subscription manager provides the mobile subscription information.
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -119,13 +118,12 @@
     public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE;
 
     /**
-     * Indicates the caller wants the default phone id.
-     * Used in SubscriptionController and Phone but do we really need it???
+     * Indicates the default phone id.
      * @hide
      */
     public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE;
 
-    /** Indicates the caller wants the default slot id. NOT used remove? */
+    /** Indicates the default slot index. */
     /** @hide */
     public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE;
 
@@ -141,29 +139,10 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static final Uri CONTENT_URI = SimInfo.CONTENT_URI;
 
-    private static final String CACHE_KEY_DEFAULT_SUB_ID_PROPERTY =
-            "cache_key.telephony.get_default_sub_id";
-
-    private static final String CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY =
-            "cache_key.telephony.get_default_data_sub_id";
-
-    private static final String CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY =
-            "cache_key.telephony.get_default_sms_sub_id";
-
-    private static final String CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY =
-            "cache_key.telephony.get_active_data_sub_id";
-
-    private static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
-            "cache_key.telephony.get_slot_index";
-
     /** The IPC cache key shared by all subscription manager service cacheable properties. */
     private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY =
             "cache_key.telephony.subscription_manager_service";
 
-    /** The temporarily cache key to indicate whether subscription manager service is enabled. */
-    private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_ENABLED_PROPERTY =
-            "cache_key.telephony.subscription_manager_service_enabled";
-
     /** @hide */
     public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
 
@@ -273,83 +252,41 @@
         }
     }
 
-    private static VoidPropertyInvalidatedCache<Integer> sDefaultSubIdCache =
-            new VoidPropertyInvalidatedCache<>(ISub::getDefaultSubId,
-                    CACHE_KEY_DEFAULT_SUB_ID_PROPERTY,
-                    INVALID_SUBSCRIPTION_ID);
-
     private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSubIdCache =
             new VoidPropertyInvalidatedCache<>(ISub::getDefaultSubId,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
-    private static VoidPropertyInvalidatedCache<Integer> sDefaultDataSubIdCache =
-            new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId,
-                    CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY,
-                    INVALID_SUBSCRIPTION_ID);
-
     private static VoidPropertyInvalidatedCache<Integer> sGetDefaultDataSubIdCache =
             new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
-    private static VoidPropertyInvalidatedCache<Integer> sDefaultSmsSubIdCache =
-            new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId,
-                    CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY,
-                    INVALID_SUBSCRIPTION_ID);
-
     private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSmsSubIdCache =
             new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
-    private static VoidPropertyInvalidatedCache<Integer> sActiveDataSubIdCache =
-            new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId,
-                    CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY,
-                    INVALID_SUBSCRIPTION_ID);
-
     private static VoidPropertyInvalidatedCache<Integer> sGetActiveDataSubscriptionIdCache =
             new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
-    private static IntegerPropertyInvalidatedCache<Integer> sSlotIndexCache =
-            new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex,
-                    CACHE_KEY_SLOT_INDEX_PROPERTY,
-                    INVALID_SIM_SLOT_INDEX);
-
     private static IntegerPropertyInvalidatedCache<Integer> sGetSlotIndexCache =
             new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SIM_SLOT_INDEX);
 
-    private static IntegerPropertyInvalidatedCache<Integer> sSubIdCache =
-            new IntegerPropertyInvalidatedCache<>(ISub::getSubId,
-                    CACHE_KEY_SLOT_INDEX_PROPERTY,
-                    INVALID_SUBSCRIPTION_ID);
-
     private static IntegerPropertyInvalidatedCache<Integer> sGetSubIdCache =
             new IntegerPropertyInvalidatedCache<>(ISub::getSubId,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
-    /** Cache depends on getDefaultSubId, so we use the defaultSubId cache key */
-    private static IntegerPropertyInvalidatedCache<Integer> sPhoneIdCache =
-            new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId,
-                    CACHE_KEY_DEFAULT_SUB_ID_PROPERTY,
-                    INVALID_PHONE_INDEX);
-
     private static IntegerPropertyInvalidatedCache<Integer> sGetPhoneIdCache =
             new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_PHONE_INDEX);
 
-    //TODO: Removed before U AOSP public release.
-    private static VoidPropertyInvalidatedCache<Boolean> sIsSubscriptionManagerServiceEnabled =
-            new VoidPropertyInvalidatedCache<>(ISub::isSubscriptionManagerServiceEnabled,
-                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_ENABLED_PROPERTY,
-                    false);
-
     /**
      * Generates a content {@link Uri} used to receive updates on simInfo change
      * on the given subscriptionId
@@ -1455,17 +1392,6 @@
         mContext = context;
     }
 
-    /**
-     * @return {@code true} if the new subscription manager service is used. This is temporary and
-     * will be removed before Android 14 release.
-     *
-     * @hide
-     */
-    //TODO: Removed before U AOSP public release.
-    public static boolean isSubscriptionManagerServiceEnabled() {
-        return sIsSubscriptionManagerServiceEnabled.query(null);
-    }
-
     private NetworkPolicyManager getNetworkPolicyManager() {
         return (NetworkPolicyManager) mContext
                 .getSystemService(Context.NETWORK_POLICY_SERVICE);
@@ -1520,7 +1446,7 @@
                     + " listener=" + listener);
         }
         // We use the TelephonyRegistry as it runs in the system and thus is always
-        // available. Where as SubscriptionController could crash and not be available
+        // available.
         TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
                 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (telephonyRegistryManager != null) {
@@ -1550,7 +1476,7 @@
                     + " listener=" + listener);
         }
         // We use the TelephonyRegistry as it runs in the system and thus is always
-        // available where as SubscriptionController could crash and not be available
+        // available.
         TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
                 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (telephonyRegistryManager != null) {
@@ -1608,7 +1534,7 @@
         }
 
         // We use the TelephonyRegistry as it runs in the system and thus is always
-        // available where as SubscriptionController could crash and not be available
+        // available.
         TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
                 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (telephonyRegistryManager != null) {
@@ -2149,9 +2075,9 @@
                 Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
                 return;
             }
-            int result = iSub.removeSubInfo(uniqueId, subscriptionType);
-            if (result < 0) {
-                Log.e(LOG_TAG, "Removal of subscription didn't succeed: error = " + result);
+            boolean result = iSub.removeSubInfo(uniqueId, subscriptionType);
+            if (!result) {
+                Log.e(LOG_TAG, "Removal of subscription didn't succeed");
             } else {
                 logd("successfully removed subscription");
             }
@@ -2236,8 +2162,7 @@
      * subscriptionId doesn't have an associated slot index.
      */
     public static int getSlotIndex(int subscriptionId) {
-        if (isSubscriptionManagerServiceEnabled()) return sGetSlotIndexCache.query(subscriptionId);
-        return sSlotIndexCache.query(subscriptionId);
+        return sGetSlotIndexCache.query(subscriptionId);
     }
 
     /**
@@ -2294,15 +2219,13 @@
             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }
 
-        if (isSubscriptionManagerServiceEnabled()) return sGetSubIdCache.query(slotIndex);
-        return sSubIdCache.query(slotIndex);
+        return sGetSubIdCache.query(slotIndex);
     }
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static int getPhoneId(int subId) {
-        if (isSubscriptionManagerServiceEnabled()) return sGetPhoneIdCache.query(subId);
-        return sPhoneIdCache.query(subId);
+        return sGetPhoneIdCache.query(subId);
     }
 
     private static void logd(String msg) {
@@ -2323,8 +2246,7 @@
      * @return the "system" default subscription id.
      */
     public static int getDefaultSubscriptionId() {
-        if (isSubscriptionManagerServiceEnabled()) return sGetDefaultSubIdCache.query(null);
-        return sDefaultSubIdCache.query(null);
+        return sGetDefaultSubIdCache.query(null);
     }
 
     /**
@@ -2412,8 +2334,7 @@
      * @return the default SMS subscription Id.
      */
     public static int getDefaultSmsSubscriptionId() {
-        if (isSubscriptionManagerServiceEnabled()) return sGetDefaultSmsSubIdCache.query(null);
-        return sDefaultSmsSubIdCache.query(null);
+        return sGetDefaultSmsSubIdCache.query(null);
     }
 
     /**
@@ -2447,8 +2368,7 @@
      * @return the default data subscription Id.
      */
     public static int getDefaultDataSubscriptionId() {
-        if (isSubscriptionManagerServiceEnabled()) return sGetDefaultDataSubIdCache.query(null);
-        return sDefaultDataSubIdCache.query(null);
+        return sGetDefaultDataSubIdCache.query(null);
     }
 
     /**
@@ -3912,10 +3832,7 @@
      * @see TelephonyCallback.ActiveDataSubscriptionIdListener
      */
     public static int getActiveDataSubscriptionId() {
-        if (isSubscriptionManagerServiceEnabled()) {
-            return sGetActiveDataSubscriptionIdCache.query(null);
-        }
-        return sActiveDataSubIdCache.query(null);
+        return sGetActiveDataSubscriptionIdCache.query(null);
     }
 
     /**
@@ -3934,61 +3851,16 @@
     }
 
     /** @hide */
-    public static void invalidateDefaultSubIdCaches() {
-        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DEFAULT_SUB_ID_PROPERTY);
-    }
-
-    /** @hide */
-    public static void invalidateDefaultDataSubIdCaches() {
-        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY);
-    }
-
-    /** @hide */
-    public static void invalidateDefaultSmsSubIdCaches() {
-        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY);
-    }
-
-    /** @hide */
-    public static void invalidateActiveDataSubIdCaches() {
-        if (isSubscriptionManagerServiceEnabled()) {
-            PropertyInvalidatedCache.invalidateCache(
-                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY);
-        } else {
-            PropertyInvalidatedCache.invalidateCache(CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY);
-        }
-    }
-
-    /** @hide */
-    public static void invalidateSlotIndexCaches() {
-        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SLOT_INDEX_PROPERTY);
-    }
-
-    /** @hide */
     public static void invalidateSubscriptionManagerServiceCaches() {
         PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY);
     }
 
-    /** @hide */
-    //TODO: Removed before U AOSP public release.
-    public static void invalidateSubscriptionManagerServiceEnabledCaches() {
-        PropertyInvalidatedCache.invalidateCache(
-                CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_ENABLED_PROPERTY);
-    }
-
     /**
      * Allows a test process to disable client-side caching operations.
      *
      * @hide
      */
     public static void disableCaching() {
-        sDefaultSubIdCache.disableLocal();
-        sDefaultDataSubIdCache.disableLocal();
-        sActiveDataSubIdCache.disableLocal();
-        sDefaultSmsSubIdCache.disableLocal();
-        sSlotIndexCache.disableLocal();
-        sSubIdCache.disableLocal();
-        sPhoneIdCache.disableLocal();
-
         sGetDefaultSubIdCache.disableLocal();
         sGetDefaultDataSubIdCache.disableLocal();
         sGetActiveDataSubscriptionIdCache.disableLocal();
@@ -3996,8 +3868,6 @@
         sGetSlotIndexCache.disableLocal();
         sGetSubIdCache.disableLocal();
         sGetPhoneIdCache.disableLocal();
-
-        sIsSubscriptionManagerServiceEnabled.disableLocal();
     }
 
     /**
@@ -4005,14 +3875,6 @@
      *
      * @hide */
     public static void clearCaches() {
-        sDefaultSubIdCache.clear();
-        sDefaultDataSubIdCache.clear();
-        sActiveDataSubIdCache.clear();
-        sDefaultSmsSubIdCache.clear();
-        sSlotIndexCache.clear();
-        sSubIdCache.clear();
-        sPhoneIdCache.clear();
-
         sGetDefaultSubIdCache.clear();
         sGetDefaultDataSubIdCache.clear();
         sGetActiveDataSubscriptionIdCache.clear();
@@ -4020,8 +3882,6 @@
         sGetSlotIndexCache.clear();
         sGetSubIdCache.clear();
         sGetPhoneIdCache.clear();
-
-        sIsSubscriptionManagerServiceEnabled.clear();
     }
 
     /**
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/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6a5380d..21a6b44 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -129,9 +129,9 @@
      * @param uniqueId This is the unique identifier for the subscription within the specific
      *                      subscription type.
      * @param subscriptionType the type of subscription to be removed
-     * @return 0 if success, < 0 on error.
+     * @return true if success, false on error.
      */
-    int removeSubInfo(String uniqueId, int subscriptionType);
+    boolean removeSubInfo(String uniqueId, int subscriptionType);
 
     /**
      * Set SIM icon tint color by simInfo index
@@ -260,7 +260,7 @@
 
     int[] getActiveSubIdList(boolean visibleOnly);
 
-    int setSubscriptionProperty(int subId, String propKey, String propValue);
+    void setSubscriptionProperty(int subId, String propKey, String propValue);
 
     String getSubscriptionProperty(int subId, String propKey, String callingPackage,
             String callingFeatureId);
@@ -353,13 +353,6 @@
        */
        List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle);
 
-       /**
-        * @return {@code true} if using SubscriptionManagerService instead of
-        * SubscriptionController.
-        */
-       //TODO: Removed before U AOSP public release.
-       boolean isSubscriptionManagerServiceEnabled();
-
       /**
        * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
        * configs to device for all existing SIMs in the subscription database
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/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/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 18e49fe..ae9ca80 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
@@ -102,7 +102,7 @@
 
     @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
-    @Postsubmit
+    @Ignore("Not applicable to this CUJ. App is full screen at the end")
     @Test
     override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
 
@@ -127,11 +127,11 @@
     @Test
     override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Ignore("Not applicable to this CUJ. App is full screen at the end")
     @Test
     override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
 
-    @Postsubmit
+    @Ignore("Not applicable to this CUJ. App is full screen at the end")
     @Test
     override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
 
@@ -145,7 +145,7 @@
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
-    @Postsubmit
+    @Ignore("Not applicable to this CUJ. App is full screen at the end")
     @Test
     override fun navBarWindowIsVisibleAtStartAndEnd() {
         super.navBarWindowIsVisibleAtStartAndEnd()
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/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 25373f9..554f64e 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -15,7 +15,7 @@
 
 cc_binary_host {
     name: "validatekeymaps",
-
+    cpp_std: "c++20",
     srcs: ["Main.cpp"],
 
     cflags: [
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;