Merge "Move package parsing implementations to internal" into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 03891bb..e3ba50d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -319,8 +320,8 @@
             final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid);
             mPackageStoppedState.add(uid, packageName, isStopped);
             return isStopped;
-        } catch (IllegalArgumentException e) {
-            Slog.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.e(TAG, "Couldn't determine stopped state for unknown package: " + packageName);
             return false;
         }
     }
diff --git a/core/api/current.txt b/core/api/current.txt
index 83e3fab..7f261d4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5285,9 +5285,10 @@
     field public static final int START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START = 21; // 0x15
     field public static final int START_TIMESTAMP_RESERVED_RANGE_SYSTEM = 20; // 0x14
     field public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7; // 0x7
-    field public static final int START_TYPE_COLD = 0; // 0x0
-    field public static final int START_TYPE_HOT = 2; // 0x2
-    field public static final int START_TYPE_WARM = 1; // 0x1
+    field public static final int START_TYPE_COLD = 1; // 0x1
+    field public static final int START_TYPE_HOT = 3; // 0x3
+    field public static final int START_TYPE_UNSET = 0; // 0x0
+    field public static final int START_TYPE_WARM = 2; // 0x2
   }
 
   public final class AsyncNotedAppOp implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7237af3..225b31c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13051,7 +13051,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.HotwordDetector.Callback);
     method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.VisualQueryDetector createVisualQueryDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VisualQueryDetector.Callback);
-    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setIsReceiveSandboxedTrainingDataAllowed(boolean);
+    method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setShouldReceiveSandboxedTrainingData(boolean);
   }
 
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8b39ed6..6ddb36a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4077,7 +4077,7 @@
         final LoadedApk sdkApk = getPackageInfo(
                 contextInfo.getSdkApplicationInfo(),
                 r.packageInfo.getCompatibilityInfo(),
-                ActivityContextInfo.CONTEXT_FLAGS);
+                contextInfo.getContextFlags());
 
         final ContextImpl activityContext = ContextImpl.createActivityContext(
                 this, sdkApk, r.activityInfo, r.token, displayId, r.overrideConfig);
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index c8317c8..656feb0 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -104,14 +104,17 @@
     /** Process started due to Activity started for any reason not explicitly listed. */
     public static final int START_REASON_START_ACTIVITY = 11;
 
+    /** Start type not yet set. */
+    public static final int START_TYPE_UNSET = 0;
+
     /** Process started from scratch. */
-    public static final int START_TYPE_COLD = 0;
+    public static final int START_TYPE_COLD = 1;
 
     /** Process retained minimally SavedInstanceState. */
-    public static final int START_TYPE_WARM = 1;
+    public static final int START_TYPE_WARM = 2;
 
     /** Process brought back to foreground. */
-    public static final int START_TYPE_HOT = 2;
+    public static final int START_TYPE_HOT = 3;
 
     /**
      * Default. The system always creates a new instance of the activity in the target task and
@@ -277,6 +280,7 @@
     @IntDef(
             prefix = {"START_TYPE_"},
             value = {
+                START_TYPE_UNSET,
                 START_TYPE_COLD,
                 START_TYPE_WARM,
                 START_TYPE_HOT,
@@ -769,6 +773,7 @@
 
     private static String startTypeToString(@StartType int startType) {
         return switch (startType) {
+            case START_TYPE_UNSET -> "UNSET";
             case START_TYPE_COLD -> "COLD";
             case START_TYPE_WARM -> "WARM";
             case START_TYPE_HOT -> "HOT";
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index d935449..53a21cd 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.annotation.DrawableRes;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -390,7 +392,7 @@
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public void setType(@Type int type) {
-        mType = type;
+        mType = checkValidType(type);
     }
 
     /**
@@ -451,6 +453,24 @@
         mAllowManualInvocation = allowManualInvocation;
     }
 
+    /** @hide */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public void validate() {
+        if (Flags.modesApi()) {
+            checkValidType(mType);
+        }
+    }
+
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @Type
+    private static int checkValidType(@Type int type) {
+        checkArgument(type >= TYPE_UNKNOWN && type <= TYPE_MANAGED,
+                "Rule type must be one of TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, "
+                        + "TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME, TYPE_DRIVING, TYPE_IMMERSIVE, "
+                        + "TYPE_THEATER, or TYPE_MANAGED");
+        return type;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -703,10 +723,10 @@
         }
 
         /**
-         * Sets the type of the rule
+         * Sets the type of the rule.
          */
         public @NonNull Builder setType(@Type int type) {
-            mType = type;
+            mType = checkValidType(type);
             return this;
         }
 
@@ -714,7 +734,7 @@
          * Sets a user visible description of when this rule will be active
          * (see {@link Condition#STATE_TRUE}).
          *
-         * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
+         * <p>A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
          * "When connected to [Car Name]".
          */
         public @NonNull Builder setTriggerDescription(@Nullable String description) {
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index e9f419e..6f7299a 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -64,6 +64,7 @@
     PendingIntent getActivityLaunchIntent(String callingPackage, in ComponentName component,
             in UserHandle user);
     LauncherUserInfo getLauncherUserInfo(in UserHandle user);
+    List<String> getPreInstalledSystemPackages(in UserHandle user);
     void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
             String callingFeatureId, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 0cd4358..ccc8f09 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -800,6 +800,29 @@
     }
 
     /**
+     * Returns the list of the system packages that are installed at user creation.
+     *
+     * <p>An empty list denotes that all system packages are installed for that user at creation.
+     * This behaviour is inherited from the underlining UserManager API.
+     *
+     * @param userHandle the user for which installed system packages are required.
+     * @return {@link List} of {@link String}, representing the package name of the installed
+     *        package. Can be empty but not null.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+    public List<String> getPreInstalledSystemPackages(@NonNull UserHandle userHandle) {
+        if (DEBUG) {
+            Log.i(TAG, "getPreInstalledSystemPackages for user: " + userHandle);
+        }
+        try {
+            return mService.getPreInstalledSystemPackages(userHandle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
      * returns null.
      *
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 93fbe8a..7cf10d8 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1216,7 +1216,7 @@
      * <ul>
      * <li>Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }</li>
      * <li>All mandatory stream combinations for this specific capability as per
-     *   <a href="CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li>
+     *   <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li>
      * <li>In case the device is not able to capture some combination of supported
      *   standard 8-bit and/or 10-bit dynamic range profiles within the same capture request,
      *   then those constraints must be listed in
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 06397c9..ded96a2 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1484,7 +1484,7 @@
      * <p>To start a CaptureSession with a target FPS range different from the
      * capture request template's default value, the application
      * is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the target fps range before creating the capture session. The aeTargetFpsRange is
      * typically a session parameter. Specifying it at session creation time helps avoid
      * session reconfiguration delays in cases like 60fps or high speed recording.</p>
@@ -2161,7 +2161,7 @@
      * OFF if the recording output is not stabilized, or if there are no output
      * Surface types that can be stabilized.</p>
      * <p>The application is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the desired video stabilization mode before creating the capture session.
      * Video stabilization mode is a session parameter on many devices. Specifying
      * it at session creation time helps avoid reconfiguration delay caused by difference
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ab4406c3..1d26d69 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -899,7 +899,7 @@
      * <p>To start a CaptureSession with a target FPS range different from the
      * capture request template's default value, the application
      * is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the target fps range before creating the capture session. The aeTargetFpsRange is
      * typically a session parameter. Specifying it at session creation time helps avoid
      * session reconfiguration delays in cases like 60fps or high speed recording.</p>
@@ -2382,7 +2382,7 @@
      * OFF if the recording output is not stabilized, or if there are no output
      * Surface types that can be stabilized.</p>
      * <p>The application is strongly recommended to call
-     * {@link SessionConfiguration#setSessionParameters }
+     * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters }
      * with the desired video stabilization mode before creating the capture session.
      * Video stabilization mode is a session parameter on many devices. Specifying
      * it at session creation time helps avoid reconfiguration delay caused by difference
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 531626b..6e771f8 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2014, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +16,8 @@
 
 package android.service.notification;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -117,7 +119,7 @@
 
     /** The source of, or reason for, the state change represented by this Condition. **/
     @FlaggedApi(Flags.FLAG_MODES_API)
-    public final @Source int source;
+    public final @Source int source; // default = SOURCE_UNKNOWN
 
     /**
      * The maximum string length for any string contained in this condition.
@@ -179,7 +181,7 @@
         this.line2 = getTrimmedString(line2);
         this.icon = icon;
         this.state = state;
-        this.source = source;
+        this.source = checkValidSource(source);
         this.flags = flags;
     }
 
@@ -197,10 +199,26 @@
                 source.readInt());
     }
 
+    /** @hide */
+    public void validate() {
+        if (Flags.modesApi()) {
+            checkValidSource(source);
+        }
+    }
+
     private static boolean isValidState(int state) {
         return state >= STATE_FALSE && state <= STATE_ERROR;
     }
 
+    private static int checkValidSource(@Source int source) {
+        if (Flags.modesApi()) {
+            checkArgument(source >= SOURCE_UNKNOWN && source <= SOURCE_CONTEXT,
+                    "Condition source must be one of SOURCE_UNKNOWN, SOURCE_USER_ACTION, "
+                            + "SOURCE_SCHEDULE, or SOURCE_CONTEXT");
+        }
+        return source;
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(id, 0);
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index a5b087c..fcdc5fe 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1980,6 +1980,7 @@
         @Nullable public ZenDeviceEffects zenDeviceEffects;
         public boolean modified;    // rule has been modified from initial creation
         public String pkg;
+        @AutomaticZenRule.Type
         public int type = AutomaticZenRule.TYPE_UNKNOWN;
         public String triggerDescription;
         public String iconResName;
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index fba0923..75ab48a 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -1042,13 +1042,13 @@
     @SystemApi
     @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
     @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
-    public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) {
-        Log.i(TAG, "setIsReceiveSandboxedTrainingDataAllowed to " + allowed);
+    public void setShouldReceiveSandboxedTrainingData(boolean allowed) {
+        Log.i(TAG, "setShouldReceiveSandboxedTrainingData to " + allowed);
         if (mSystemService == null) {
             throw new IllegalStateException("Not available until onReady() is called");
         }
         try {
-            mSystemService.setIsReceiveSandboxedTrainingDataAllowed(allowed);
+            mSystemService.setShouldReceiveSandboxedTrainingData(allowed);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index dbacca5..9bf43a3 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -162,6 +162,8 @@
      * @param flags See {@code View#startDragAndDrop}
      * @param surface Surface containing drag shadow image
      * @param touchSource See {@code InputDevice#getSource()}
+     * @param touchDeviceId device ID of last touch event
+     * @param pointerId pointer ID of last touch event
      * @param touchX X coordinate of last touch point
      * @param touchY Y coordinate of last touch point
      * @param thumbCenterX X coordinate for the position within the shadow image that should be
@@ -171,9 +173,9 @@
      * @param data Data transferred by drag and drop
      * @return Token of drag operation which will be passed to cancelDragAndDrop.
      */
-    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     IBinder performDrag(IWindow window, int flags, in SurfaceControl surface, int touchSource,
-            float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data);
+            int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX,
+            float thumbCenterY, in ClipData data);
 
     /**
      * Drops the content of the current drag operation for accessibility
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 75f8eba..bb5ee03 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28340,6 +28340,8 @@
                 IBinder token = mAttachInfo.mSession.performDrag(
                         mAttachInfo.mWindow, flags, null,
                         mAttachInfo.mViewRootImpl.getLastTouchSource(),
+                        mAttachInfo.mViewRootImpl.getLastTouchDeviceId(),
+                        mAttachInfo.mViewRootImpl.getLastTouchPointerId(),
                         0f, 0f, 0f, 0f, data);
                 if (ViewDebug.DEBUG_DRAG) {
                     Log.d(VIEW_LOG_TAG, "startDragAndDrop via a11y action returned " + token);
@@ -28414,7 +28416,8 @@
             }
 
             token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
-                    root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y,
+                    root.getLastTouchSource(), root.getLastTouchDeviceId(),
+                    root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
                     shadowTouchPoint.x, shadowTouchPoint.y, data);
             if (ViewDebug.DEBUG_DRAG) {
                 Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8339d33..e83488e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -813,6 +813,8 @@
     final PointF mDragPoint = new PointF();
     final PointF mLastTouchPoint = new PointF();
     int mLastTouchSource;
+    int mLastTouchDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
+    int mLastTouchPointerId;
     /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/
     private int mLastClickToolType;
 
@@ -7163,6 +7165,8 @@
                 mLastTouchPoint.x = event.getRawX();
                 mLastTouchPoint.y = event.getRawY();
                 mLastTouchSource = event.getSource();
+                mLastTouchDeviceId = event.getDeviceId();
+                mLastTouchPointerId = event.getPointerId(0);
 
                 // Register last ACTION_UP. This will be propagated to IME.
                 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -8574,6 +8578,14 @@
         return mLastTouchSource;
     }
 
+    public int getLastTouchDeviceId() {
+        return mLastTouchDeviceId;
+    }
+
+    public int getLastTouchPointerId() {
+        return mLastTouchPointerId;
+    }
+
     /**
      * Used by InputMethodManager.
      * @hide
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 393d256..d6ac562 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -489,8 +489,9 @@
 
     @Override
     public android.os.IBinder performDrag(android.view.IWindow window, int flags,
-            android.view.SurfaceControl surface, int touchSource, float touchX, float touchY,
-            float thumbCenterX, float thumbCenterY, android.content.ClipData data) {
+            android.view.SurfaceControl surface, int touchSource, int touchDeviceId,
+            int touchPointerId, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+            android.content.ClipData data) {
         return null;
     }
 
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 82ee8fc..e92c6a6 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -397,5 +397,5 @@
       * sandboxed detection (from trusted process).
       */
       @EnforcePermission("MANAGE_HOTWORD_DETECTION")
-      void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed);
+      void setShouldReceiveSandboxedTrainingData(boolean allowed);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 28fd2b4..bf8e613 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -897,13 +897,26 @@
     }
 
     /**
-     * Returns true if {@code userHandle} is a managed profile with separate challenge.
+     * Returns true if {@code userHandle} is a profile with separate challenge.
+     * <p>
+     * Returns false if {@code userHandle} is a profile with unified challenge, a profile whose
+     * credential is not shareable with its parent, or a non-profile user.
      */
     public boolean isSeparateProfileChallengeEnabled(int userHandle) {
         return isCredentialSharableWithParent(userHandle) && hasSeparateChallenge(userHandle);
     }
 
     /**
+     * Returns true if {@code userHandle} is a profile with unified challenge.
+     * <p>
+     * Returns false if {@code userHandle} is a profile with separate challenge, a profile whose
+     * credential is not shareable with its parent, or a non-profile user.
+     */
+    public boolean isProfileWithUnifiedChallenge(int userHandle) {
+        return isCredentialSharableWithParent(userHandle) && !hasSeparateChallenge(userHandle);
+    }
+
+    /**
      * Returns true if {@code userHandle} is a managed profile with unified challenge.
      */
     public boolean isManagedProfileWithUnifiedChallenge(int userHandle) {
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index c88763c..18d5f6d 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -134,12 +134,12 @@
     }
 
     /**
-     * Creates a LockscreenCredential object representing a managed password for profile with
-     * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now.
-     * TODO: consider add a new credential type for this. This can then supersede the
-     * isLockTiedToParent argument in various places in LSS.
+     * Creates a LockscreenCredential object representing the system-generated, system-managed
+     * password for a profile with unified challenge. This credential has type {@code
+     * CREDENTIAL_TYPE_PASSWORD} for now. TODO: consider add a new credential type for this. This
+     * can then supersede the isLockTiedToParent argument in various places in LSS.
      */
-    public static LockscreenCredential createManagedPassword(@NonNull byte[] password) {
+    public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) {
         return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
                 Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false);
     }
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1f6ac80..4596ca7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5252,6 +5252,8 @@
     <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
     <string name="zen_mode_default_every_night_name">Sleeping</string>
 
+    <!-- Zen mode - Trigger description of the rule, indicating which app owns it. [CHAR_LIMIT=100] -->
+    <string name="zen_mode_implicit_trigger_description">Managed by <xliff:g id="app_name">%1$s</xliff:g></string>
     <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
     <string name="zen_mode_implicit_activated">On</string>
     <!-- Zen mode - Condition summary when a rule is deactivated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5791ddb..fd6158d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2586,6 +2586,7 @@
   <java-symbol type="string" name="zen_mode_default_weekends_name" />
   <java-symbol type="string" name="zen_mode_default_events_name" />
   <java-symbol type="string" name="zen_mode_default_every_night_name" />
+  <java-symbol type="string" name="zen_mode_implicit_trigger_description" />
   <java-symbol type="string" name="zen_mode_implicit_activated" />
   <java-symbol type="string" name="zen_mode_implicit_deactivated" />
   <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" />
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index ba2ea88..d629f6a 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -19,15 +19,20 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertThrows;
+
 import android.content.ComponentName;
 import android.net.Uri;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.common.base.Strings;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -38,6 +43,9 @@
 public class AutomaticZenRuleTest {
     private static final String CLASS = "android.app.AutomaticZenRule";
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void testLongFields_inConstructor() {
         String longString = Strings.repeat("A", 65536);
@@ -100,6 +108,7 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
     public void testLongInputsFromParcel() {
         // Create a rule with long fields, set directly via reflection so that we can confirm that
         // a rule with too-long fields that comes in via a parcel has its fields truncated directly.
@@ -152,6 +161,60 @@
                 fromParcel.getOwner().getPackageName().length());
         assertEquals(AutomaticZenRule.MAX_STRING_LENGTH,
                 fromParcel.getOwner().getClassName().length());
-        assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length());
+        assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, fromParcel.getTriggerDescription().length());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_builderWithValidType_succeeds() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+                .setType(AutomaticZenRule.TYPE_BEDTIME)
+                .build();
+        rule.validate(); // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_builderWithoutType_succeeds() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build();
+        rule.validate(); // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_constructorWithoutType_succeeds() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule("rule", new ComponentName("pkg", "cps"),
+                new ComponentName("pkg", "activity"), Uri.parse("condition"), null,
+                NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+        rule.validate(); // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_invalidType_throws() throws Exception {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build();
+
+        // Set the field via reflection.
+        Field typeField = AutomaticZenRule.class.getDeclaredField("mType");
+        typeField.setAccessible(true);
+        typeField.set(rule, 100);
+
+        assertThrows(IllegalArgumentException.class, rule::validate);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void setType_invalidType_throws() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build();
+
+        assertThrows(IllegalArgumentException.class, () -> rule.setType(100));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void setTypeBuilder_invalidType_throws() {
+        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("rule", Uri.parse("uri"));
+
+        assertThrows(IllegalArgumentException.class, () -> builder.setType(100));
     }
 }
diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java
index 612562e..e94273e 100644
--- a/core/tests/coretests/src/android/service/notification/ConditionTest.java
+++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java
@@ -21,9 +21,12 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertThrows;
+
 import android.app.Flags;
 import android.net.Uri;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,6 +39,7 @@
 import org.junit.runner.RunWith;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -194,4 +198,59 @@
         Condition fromParcel = new Condition(parcel);
         assertThat(fromParcel).isEqualTo(cond);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_unspecifiedSource_succeeds() {
+        new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE);
+        // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_validSource_succeeds() {
+        new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE, Condition.SOURCE_CONTEXT);
+        // No exception.
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_invalidSource_throws() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new Condition(Uri.parse("uri"), "Summary", Condition.STATE_TRUE, 1000));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void constructor_parcelWithInvalidSource_throws() {
+        Condition original = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE,
+                Condition.SOURCE_SCHEDULE);
+        Parcel parcel = Parcel.obtain();
+        original.writeToParcel(parcel, 0);
+
+        // Tweak the parcel to contain and invalid source value.
+        parcel.setDataPosition(parcel.dataPosition() - 8); // going back two int fields.
+        parcel.writeInt(100);
+        parcel.setDataPosition(0);
+
+        assertThrows(IllegalArgumentException.class, () -> new Condition(parcel));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void validate_invalidSource_throws() throws Exception {
+        Condition condition = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE,
+                Condition.SOURCE_SCHEDULE);
+
+        Field typeField = Condition.class.getDeclaredField("source");
+
+        // Reflection on reflection (ugh) to make a final field non-final
+        Field fieldAccessFlagsField = Field.class.getDeclaredField("accessFlags");
+        fieldAccessFlagsField.setAccessible(true);
+        fieldAccessFlagsField.setInt(typeField, typeField.getModifiers() & ~Modifier.FINAL);
+
+        typeField.setInt(condition, 30);
+
+        assertThrows(IllegalArgumentException.class, condition::validate);
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 15d14e8..b315f94 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -241,7 +241,7 @@
         for (int i = 0; i < displays.length; i++) {
             DisplayAddress.Physical address =
                     (DisplayAddress.Physical) displays[i].getAddress();
-            if (mRearDisplayAddress == address.getPhysicalDisplayId()) {
+            if (address != null && mRearDisplayAddress == address.getPhysicalDisplayId()) {
                 rearDisplayMetrics = new DisplayMetrics();
                 final Display rearDisplay = displays[i];
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 0f0fbd9c..f801b0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -83,6 +83,7 @@
     private int mStartPos;
     private GestureDetector mDoubleTapDetector;
     private boolean mInteractive;
+    private boolean mHideHandle;
     private boolean mSetTouchRegion = true;
     private int mLastDraggingPosition;
     private int mHandleRegionWidth;
@@ -211,11 +212,8 @@
     }
 
     /** Sets up essential dependencies of the divider bar. */
-    public void setup(
-            SplitLayout layout,
-            SplitWindowManager splitWindowManager,
-            SurfaceControlViewHost viewHost,
-            InsetsState insetsState) {
+    public void setup(SplitLayout layout, SplitWindowManager splitWindowManager,
+            SurfaceControlViewHost viewHost, InsetsState insetsState) {
         mSplitLayout = layout;
         mSplitWindowManager = splitWindowManager;
         mViewHost = viewHost;
@@ -277,6 +275,7 @@
                 R.dimen.docked_stack_divider_lift_elevation);
         mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
         mInteractive = true;
+        mHideHandle = false;
         setOnTouchListener(this);
         mHandle.setAccessibilityDelegate(mHandleDelegate);
         setWillNotDraw(false);
@@ -469,10 +468,11 @@
     void setInteractive(boolean interactive, boolean hideHandle, String from) {
         if (interactive == mInteractive) return;
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive",
-                from);
+                "Set divider bar %s hide handle=%b from %s",
+                interactive ? "interactive" : "non-interactive", hideHandle, from);
         mInteractive = interactive;
-        if (!mInteractive && hideHandle && mMoving) {
+        mHideHandle = hideHandle;
+        if (!mInteractive && mHideHandle && mMoving) {
             final int position = mSplitLayout.getDividePosition();
             mSplitLayout.flingDividePosition(
                     mLastDraggingPosition,
@@ -482,7 +482,15 @@
             mMoving = false;
         }
         releaseTouching();
-        mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE);
+        mHandle.setVisibility(!mInteractive && mHideHandle ? View.INVISIBLE : View.VISIBLE);
+    }
+
+    boolean isInteractive() {
+        return mInteractive;
+    }
+
+    boolean isHandleHidden() {
+        return mHideHandle;
     }
 
     private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b699533..53caddb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -59,6 +59,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.animation.Interpolators;
@@ -70,6 +71,7 @@
 import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
 import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -420,7 +422,7 @@
     public void init() {
         if (mInitialized) return;
         mInitialized = true;
-        mSplitWindowManager.init(this, mInsetsState);
+        mSplitWindowManager.init(this, mInsetsState, false /* isRestoring */);
         mDisplayImeController.addPositionProcessor(mImePositionProcessor);
     }
 
@@ -442,14 +444,19 @@
     }
 
     /** Releases and re-inflates {@link DividerView} on the root surface. */
-    public void update(SurfaceControl.Transaction t) {
+    public void update(SurfaceControl.Transaction t, boolean resetImePosition) {
         if (!mInitialized) {
             init();
             return;
         }
         mSplitWindowManager.release(t);
-        mImePositionProcessor.reset();
-        mSplitWindowManager.init(this, mInsetsState);
+        if (resetImePosition) {
+            mImePositionProcessor.reset();
+        }
+        mSplitWindowManager.init(this, mInsetsState, true /* isRestoring */);
+        // Update the surface positions again after recreating the divider in case nothing else
+        // triggers it
+        mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
     }
 
     @Override
@@ -868,6 +875,9 @@
         pw.println(prefix + TAG + ":");
         pw.println(innerPrefix + "mAllowLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait);
         pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit);
+        pw.println(innerPrefix + "mFreezeDividerWindow=" + mFreezeDividerWindow);
+        pw.println(innerPrefix + "mDimNonImeSide=" + mDimNonImeSide);
+        pw.println(innerPrefix + "mDividerPosition=" + mDividerPosition);
         pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString());
         pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString());
         pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString());
@@ -1151,14 +1161,16 @@
             mTargetYOffset = needOffset ? getTargetYOffset() : 0;
 
             if (mTargetYOffset != mLastYOffset) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "Split IME animation starting, fromY=%d toY=%d",
+                        mLastYOffset, mTargetYOffset);
                 // Freeze the configuration size with offset to prevent app get a configuration
                 // changed or relaunch. This is required to make sure client apps will calculate
                 // insets properly after layout shifted.
                 if (mTargetYOffset == 0) {
                     mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
                 } else {
-                    mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset - mLastYOffset,
-                            SplitLayout.this);
+                    mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this);
                 }
             }
 
@@ -1183,6 +1195,8 @@
         public void onImeEndPositioning(int displayId, boolean cancel,
                 SurfaceControl.Transaction t) {
             if (displayId != mDisplayId || !mHasImeFocus || cancel) return;
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                    "Split IME animation ending, canceled=%b", cancel);
             onProgress(1.0f);
             mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 00361d9..8fb9bda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -62,6 +62,10 @@
     // Used to "pass" a transaction to WWM.remove so that view removal can be synchronized.
     private SurfaceControl.Transaction mSyncTransaction = null;
 
+    // For saving/restoring state
+    private boolean mLastDividerInteractive = true;
+    private boolean mLastDividerHandleHidden;
+
     public interface ParentContainerCallbacks {
         void attachToParentSurface(SurfaceControl.Builder b);
         void onLeashReady(SurfaceControl leash);
@@ -107,7 +111,7 @@
     }
 
     /** Inflates {@link DividerView} on to the root surface. */
-    void init(SplitLayout splitLayout, InsetsState insetsState) {
+    void init(SplitLayout splitLayout, InsetsState insetsState, boolean isRestoring) {
         if (mDividerView != null || mViewHost != null) {
             throw new UnsupportedOperationException(
                     "Try to inflate divider view again without release first");
@@ -130,6 +134,10 @@
         lp.accessibilityTitle = mContext.getResources().getString(R.string.accessibility_divider);
         mViewHost.setView(mDividerView, lp);
         mDividerView.setup(splitLayout, this, mViewHost, insetsState);
+        if (isRestoring) {
+            mDividerView.setInteractive(mLastDividerInteractive, mLastDividerHandleHidden,
+                    "restore_setup");
+        }
     }
 
     /**
@@ -138,6 +146,8 @@
      */
     void release(@Nullable SurfaceControl.Transaction t) {
         if (mDividerView != null) {
+            mLastDividerInteractive = mDividerView.isInteractive();
+            mLastDividerHandleHidden = mDividerView.isHandleHidden();
             mDividerView = null;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 449bef5..77427d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1666,7 +1666,7 @@
     }
 
     void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
-        mSplitLayout.update(finishT);
+        mSplitLayout.update(finishT, true /* resetImePosition */);
         mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
                 getMainStageBounds());
         mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash,
@@ -1860,9 +1860,10 @@
                 && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
                 && mMainStage.isActive()) {
             // Clear the divider remote animating flag as the divider will be re-rendered to apply
-            // the new rotation config.
+            // the new rotation config.  Don't reset the IME state since those updates are not in
+            // sync with task info changes.
             mIsDividerRemoteAnimating = false;
-            mSplitLayout.update(null /* t */);
+            mSplitLayout.update(null /* t */, false /* resetImePosition */);
             onLayoutSizeChanged(mSplitLayout);
         }
     }
@@ -2325,7 +2326,7 @@
      */
     public void updateSurfaces(SurfaceControl.Transaction transaction) {
         updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false);
-        mSplitLayout.update(transaction);
+        mSplitLayout.update(transaction, true /* resetImePosition */);
     }
 
     private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@@ -2598,7 +2599,9 @@
                 final TransitionInfo.Change change = info.getChanges().get(iC);
                 if (change.getMode() == TRANSIT_CHANGE
                         && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
-                    mSplitLayout.update(startTransaction);
+                    // Don't reset the IME state since those updates are not in sync with the
+                    // display change transition
+                    mSplitLayout.update(startTransaction, false /* resetImePosition */);
                 }
 
                 if (mMixedHandler.isEnteringPip(change, transitType)) {
@@ -2699,7 +2702,7 @@
                     startTransaction, finishTransaction, finishCallback)) {
                 if (mSplitTransitions.isPendingResize(transition)) {
                     // Only need to update in resize because divider exist before transition.
-                    mSplitLayout.update(startTransaction);
+                    mSplitLayout.update(startTransaction, true /* resetImePosition */);
                     startTransaction.apply();
                 }
                 return true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
index 145c8f0..636c632 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
@@ -69,7 +69,7 @@
         SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager",
                 mContext,
                 configuration, mCallbacks);
-        splitWindowManager.init(mSplitLayout, new InsetsState());
+        splitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */);
         mDividerView = spy((DividerView) splitWindowManager.getDividerView());
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index 2e5078d..150aa13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -59,7 +59,7 @@
     @Test
     @UiThreadTest
     public void testInitRelease() {
-        mSplitWindowManager.init(mSplitLayout, new InsetsState());
+        mSplitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */);
         assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull();
         mSplitWindowManager.release(null /* t */);
         assertThat(mSplitWindowManager.getSurfaceControl()).isNull();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index e07e942..2da8c8c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -156,10 +156,9 @@
                 if (customUserManager.isUserOfType(USER_TYPE_PROFILE_MANAGED)
                         && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
                     messageBuilder.append(isArchive
-                            ? getString(R.string.archive_application_text_current_user_work_profile,
-                                    userName) : getString(
-                            R.string.uninstall_application_text_current_user_work_profile,
-                            userName));
+                            ? getString(R.string.archive_application_text_current_user_work_profile)
+                            : getString(
+                                    R.string.uninstall_application_text_current_user_work_profile));
                 } else if (customUserManager.isUserOfType(USER_TYPE_PROFILE_CLONE)
                         && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
                     mIsClonedApp = true;
@@ -168,11 +167,11 @@
                 } else if (Flags.allowPrivateProfile()
                         && customUserManager.isPrivateProfile()
                         && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
-                    messageBuilder.append(isArchive ? getString(
-                            R.string.archive_application_text_current_user_private_profile,
-                            userName) : getString(
-                            R.string.uninstall_application_text_current_user_private_profile,
-                            userName));
+                    messageBuilder.append(
+                            isArchive ? getString(
+                                    R.string.archive_application_text_current_user_private_profile)
+                            : getString(
+                                R.string.uninstall_application_text_current_user_private_profile));
                 } else if (isArchive) {
                     messageBuilder.append(
                             getString(R.string.archive_application_text_user, userName));
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
index d0d2dc0..e099f11 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
@@ -20,13 +20,17 @@
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.util.Log
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOn
 
+private const val TAG = "BroadcastReceiverFlow"
+
 /**
  * A [BroadcastReceiver] flow for the given [intentFilter].
  */
@@ -39,4 +43,6 @@
     registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
 
     awaitClose { unregisterReceiver(broadcastReceiver) }
+}.catch { e ->
+    Log.e(TAG, "Error while broadcastReceiverFlow", e)
 }.conflate().flowOn(Dispatchers.Default)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
index dfaf3c6..eef5225 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
@@ -31,8 +31,10 @@
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
 import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doThrow
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
 
 @RunWith(AndroidJUnit4::class)
 class BroadcastReceiverFlowTest {
@@ -74,6 +76,18 @@
         assertThat(onReceiveIsCalled).isTrue()
     }
 
+    @Test
+    fun broadcastReceiverFlow_unregisterReceiverThrowException_noCrash() = runBlocking {
+        context.stub {
+            on { unregisterReceiver(any()) } doThrow IllegalArgumentException()
+        }
+        val flow = context.broadcastReceiverFlow(INTENT_FILTER)
+
+        flow.firstWithTimeoutOrNull()
+
+        assertThat(registeredBroadcastReceiver).isNotNull()
+    }
+
     private companion object {
         val INTENT_FILTER = IntentFilter()
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 69b61c7..2cb44ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -31,7 +31,6 @@
 import android.bluetooth.BluetoothLeBroadcastSubgroup;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfile.ServiceListener;
-import android.bluetooth.BluetoothStatusCodes;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -42,7 +41,6 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 
 import androidx.annotation.RequiresApi;
 
@@ -53,15 +51,17 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadLocalRandom;
 
 /**
- * LocalBluetoothLeBroadcast provides an interface between the Settings app
- * and the functionality of the local {@link BluetoothLeBroadcast}.
- * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback.
+ * LocalBluetoothLeBroadcast provides an interface between the Settings app and the functionality of
+ * the local {@link BluetoothLeBroadcast}. Use the {@link BluetoothLeBroadcast.Callback} to get the
+ * result callback.
  */
 public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
     private static final String TAG = "LocalBluetoothLeBroadcast";
@@ -74,11 +74,12 @@
     // Order of this profile in device profiles list
     private static final int ORDINAL = 1;
     private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
-    private static final Uri[] SETTINGS_URIS = new Uri[]{
-            Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO),
-            Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE),
-            Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME),
-    };
+    private static final Uri[] SETTINGS_URIS =
+            new Uri[] {
+                Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO),
+                Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE),
+                Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME),
+            };
 
     private BluetoothLeBroadcast mServiceBroadcast;
     private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant;
@@ -95,62 +96,82 @@
     private Executor mExecutor;
     private ContentResolver mContentResolver;
     private ContentObserver mSettingsObserver;
+    // Cached broadcast callbacks being register before service is connected.
+    private Map<BluetoothLeBroadcast.Callback, Executor> mCachedBroadcastCallbackExecutorMap =
+            new ConcurrentHashMap<>();
 
-    private final ServiceListener mServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (DEBUG) {
-                Log.d(TAG, "Bluetooth service connected: " + profile);
-            }
-            if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && !mIsBroadcastProfileReady) {
-                mServiceBroadcast = (BluetoothLeBroadcast) proxy;
-                mIsBroadcastProfileReady = true;
-                registerServiceCallBack(mExecutor, mBroadcastCallback);
-                List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata();
-                if (!metadata.isEmpty()) {
-                    updateBroadcastInfoFromBroadcastMetadata(metadata.get(0));
+    private final ServiceListener mServiceListener =
+            new ServiceListener() {
+                @Override
+                public void onServiceConnected(int profile, BluetoothProfile proxy) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Bluetooth service connected: " + profile);
+                    }
+                    if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST)
+                            && !mIsBroadcastProfileReady) {
+                        mServiceBroadcast = (BluetoothLeBroadcast) proxy;
+                        mIsBroadcastProfileReady = true;
+                        registerServiceCallBack(mExecutor, mBroadcastCallback);
+                        List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata();
+                        if (!metadata.isEmpty()) {
+                            updateBroadcastInfoFromBroadcastMetadata(metadata.get(0));
+                        }
+                        registerContentObserver();
+                        if (DEBUG) {
+                            Log.d(
+                                    TAG,
+                                    "onServiceConnected: register "
+                                            + "mCachedBroadcastCallbackExecutorMap = "
+                                            + mCachedBroadcastCallbackExecutorMap);
+                        }
+                        mCachedBroadcastCallbackExecutorMap.forEach(
+                                (callback, executor) ->
+                                        registerServiceCallBack(executor, callback));
+                    } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+                            && !mIsBroadcastAssistantProfileReady) {
+                        mIsBroadcastAssistantProfileReady = true;
+                        mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
+                        registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback);
+                    }
                 }
-                registerContentObserver();
-            } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
-                    && !mIsBroadcastAssistantProfileReady) {
-                mIsBroadcastAssistantProfileReady = true;
-                mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
-                registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback);
-            }
-        }
 
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (DEBUG) {
-                Log.d(TAG, "Bluetooth service disconnected");
-            }
-            if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) {
-                mIsBroadcastProfileReady = false;
-                unregisterServiceCallBack(mBroadcastCallback);
-            }
-            if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
-                    && mIsBroadcastAssistantProfileReady) {
-                mIsBroadcastAssistantProfileReady = false;
-                unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback);
-            }
+                @Override
+                public void onServiceDisconnected(int profile) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Bluetooth service disconnected: " + profile);
+                    }
+                    if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST)
+                            && mIsBroadcastProfileReady) {
+                        mIsBroadcastProfileReady = false;
+                        unregisterServiceCallBack(mBroadcastCallback);
+                        mCachedBroadcastCallbackExecutorMap.clear();
+                    }
+                    if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+                            && mIsBroadcastAssistantProfileReady) {
+                        mIsBroadcastAssistantProfileReady = false;
+                        unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback);
+                    }
 
-            if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) {
-                unregisterContentObserver();
-            }
-        }
-    };
+                    if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) {
+                        unregisterContentObserver();
+                    }
+                }
+            };
 
     private final BluetoothLeBroadcast.Callback mBroadcastCallback =
             new BluetoothLeBroadcast.Callback() {
                 @Override
                 public void onBroadcastStarted(int reason, int broadcastId) {
                     if (DEBUG) {
-                        Log.d(TAG,
-                                "onBroadcastStarted(), reason = " + reason + ", broadcastId = "
+                        Log.d(
+                                TAG,
+                                "onBroadcastStarted(), reason = "
+                                        + reason
+                                        + ", broadcastId = "
                                         + broadcastId);
                     }
                     setLatestBroadcastId(broadcastId);
-                    setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true);
+                    setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true);
                 }
 
                 @Override
@@ -161,8 +182,8 @@
                 }
 
                 @Override
-                public void onBroadcastMetadataChanged(int broadcastId,
-                        @NonNull BluetoothLeBroadcastMetadata metadata) {
+                public void onBroadcastMetadataChanged(
+                        int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {
                     if (DEBUG) {
                         Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId);
                     }
@@ -172,8 +193,11 @@
                 @Override
                 public void onBroadcastStopped(int reason, int broadcastId) {
                     if (DEBUG) {
-                        Log.d(TAG,
-                                "onBroadcastStopped(), reason = " + reason + ", broadcastId = "
+                        Log.d(
+                                TAG,
+                                "onBroadcastStopped(), reason = "
+                                        + reason
+                                        + ", broadcastId = "
                                         + broadcastId);
                     }
 
@@ -191,37 +215,42 @@
                 @Override
                 public void onBroadcastUpdated(int reason, int broadcastId) {
                     if (DEBUG) {
-                        Log.d(TAG,
-                                "onBroadcastUpdated(), reason = " + reason + ", broadcastId = "
+                        Log.d(
+                                TAG,
+                                "onBroadcastUpdated(), reason = "
+                                        + reason
+                                        + ", broadcastId = "
                                         + broadcastId);
                     }
                     setLatestBroadcastId(broadcastId);
-                    setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true);
+                    setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true);
                 }
 
                 @Override
                 public void onBroadcastUpdateFailed(int reason, int broadcastId) {
                     if (DEBUG) {
-                        Log.d(TAG,
-                                "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = "
+                        Log.d(
+                                TAG,
+                                "onBroadcastUpdateFailed(), reason = "
+                                        + reason
+                                        + ", broadcastId = "
                                         + broadcastId);
                     }
                 }
 
                 @Override
-                public void onPlaybackStarted(int reason, int broadcastId) {
-                }
+                public void onPlaybackStarted(int reason, int broadcastId) {}
 
                 @Override
-                public void onPlaybackStopped(int reason, int broadcastId) {
-                }
+                public void onPlaybackStopped(int reason, int broadcastId) {}
             };
 
     private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
                 @Override
-                public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId,
-                        int reason) {}
+                public void onSourceAdded(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
                 @Override
                 public void onSearchStarted(int reason) {}
 
@@ -238,38 +267,65 @@
                 public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
 
                 @Override
-                public void onSourceAddFailed(@NonNull BluetoothDevice sink,
-                        @NonNull BluetoothLeBroadcastMetadata source, int reason) {}
-
-                @Override
-                public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId,
+                public void onSourceAddFailed(
+                        @NonNull BluetoothDevice sink,
+                        @NonNull BluetoothLeBroadcastMetadata source,
                         int reason) {}
 
                 @Override
-                public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId,
-                        int reason) {}
+                public void onSourceModified(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
 
                 @Override
-                public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId,
-                        int reason) {
+                public void onSourceModifyFailed(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+                @Override
+                public void onSourceRemoved(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {
                     if (DEBUG) {
-                        Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = "
-                                + reason + ", sourceId = " + sourceId);
+                        Log.d(
+                                TAG,
+                                "onSourceRemoved(), sink = "
+                                        + sink
+                                        + ", reason = "
+                                        + reason
+                                        + ", sourceId = "
+                                        + sourceId);
                     }
                 }
 
                 @Override
-                public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId,
-                        int reason) {
+                public void onSourceRemoveFailed(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {
                     if (DEBUG) {
-                        Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = "
-                                + reason + ", sourceId = " + sourceId);
+                        Log.d(
+                                TAG,
+                                "onSourceRemoveFailed(), sink = "
+                                        + sink
+                                        + ", reason = "
+                                        + reason
+                                        + ", sourceId = "
+                                        + sourceId);
                     }
                 }
 
                 @Override
-                public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
-                        @NonNull BluetoothLeBroadcastReceiveState state) {}
+                public void onReceiveStateChanged(
+                        @NonNull BluetoothDevice sink,
+                        int sourceId,
+                        @NonNull BluetoothLeBroadcastReceiveState state) {
+                    if (DEBUG) {
+                        Log.d(
+                                TAG,
+                                "onReceiveStateChanged(), sink = "
+                                        + sink
+                                        + ", sourceId = "
+                                        + sourceId
+                                        + ", state = "
+                                        + state);
+                    }
+                }
             };
 
     private class BroadcastSettingsObserver extends ContentObserver {
@@ -296,8 +352,8 @@
         BluetoothAdapter.getDefaultAdapter()
                 .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
         BluetoothAdapter.getDefaultAdapter()
-                .getProfileProxy(context, mServiceListener,
-                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+                .getProfileProxy(
+                        context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
     }
 
     /**
@@ -312,11 +368,11 @@
         }
         String programInfo = getProgramInfo();
         if (DEBUG) {
-            Log.d(TAG,
-                    "startBroadcast: language = " + language + " ,programInfo = " + programInfo);
+            Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo);
         }
         buildContentMetadata(language, programInfo);
-        mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata,
+        mServiceBroadcast.startBroadcast(
+                mBluetoothLeAudioContentMetadata,
                 (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null);
     }
 
@@ -325,7 +381,7 @@
     }
 
     public void setProgramInfo(String programInfo) {
-        setProgramInfo(programInfo, /*updateContentResolver=*/ true);
+        setProgramInfo(programInfo, /* updateContentResolver= */ true);
     }
 
     private void setProgramInfo(String programInfo, boolean updateContentResolver) {
@@ -344,8 +400,10 @@
                 Log.d(TAG, "mContentResolver is null");
                 return;
             }
-            Settings.Secure.putString(mContentResolver,
-                    Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, programInfo);
+            Settings.Secure.putString(
+                    mContentResolver,
+                    Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO,
+                    programInfo);
         }
     }
 
@@ -354,7 +412,7 @@
     }
 
     public void setBroadcastCode(byte[] broadcastCode) {
-        setBroadcastCode(broadcastCode, /*updateContentResolver=*/ true);
+        setBroadcastCode(broadcastCode, /* updateContentResolver= */ true);
     }
 
     private void setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver) {
@@ -372,7 +430,9 @@
                 Log.d(TAG, "mContentResolver is null");
                 return;
             }
-            Settings.Secure.putString(mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
+            Settings.Secure.putString(
+                    mContentResolver,
+                    Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
                     new String(broadcastCode, StandardCharsets.UTF_8));
         }
     }
@@ -401,8 +461,10 @@
                 Log.d(TAG, "mContentResolver is null");
                 return;
             }
-            Settings.Secure.putString(mContentResolver,
-                    Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, mAppSourceName);
+            Settings.Secure.putString(
+                    mContentResolver,
+                    Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME,
+                    mAppSourceName);
         }
     }
 
@@ -427,10 +489,11 @@
         if (mBluetoothLeBroadcastMetadata == null) {
             final List<BluetoothLeBroadcastMetadata> metadataList =
                     mServiceBroadcast.getAllBroadcastMetadata();
-            mBluetoothLeBroadcastMetadata = metadataList.stream()
-                    .filter(i -> i.getBroadcastId() == mBroadcastId)
-                    .findFirst()
-                    .orElse(null);
+            mBluetoothLeBroadcastMetadata =
+                    metadataList.stream()
+                            .filter(i -> i.getBroadcastId() == mBroadcastId)
+                            .findFirst()
+                            .orElse(null);
         }
         return mBluetoothLeBroadcastMetadata;
     }
@@ -440,22 +503,27 @@
             Log.d(TAG, "updateBroadcastInfoFromContentProvider: mContentResolver is null");
             return;
         }
-        String programInfo = Settings.Secure.getString(mContentResolver,
-                Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO);
+        String programInfo =
+                Settings.Secure.getString(
+                        mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO);
         if (programInfo == null) {
             programInfo = getDefaultValueOfProgramInfo();
         }
-        setProgramInfo(programInfo, /*updateContentResolver=*/ false);
+        setProgramInfo(programInfo, /* updateContentResolver= */ false);
 
-        String prefBroadcastCode = Settings.Secure.getString(mContentResolver,
-                Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE);
-        byte[] broadcastCode = (prefBroadcastCode == null) ? getDefaultValueOfBroadcastCode()
-                : prefBroadcastCode.getBytes(StandardCharsets.UTF_8);
-        setBroadcastCode(broadcastCode, /*updateContentResolver=*/ false);
+        String prefBroadcastCode =
+                Settings.Secure.getString(
+                        mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE);
+        byte[] broadcastCode =
+                (prefBroadcastCode == null)
+                        ? getDefaultValueOfBroadcastCode()
+                        : prefBroadcastCode.getBytes(StandardCharsets.UTF_8);
+        setBroadcastCode(broadcastCode, /* updateContentResolver= */ false);
 
-        String appSourceName = Settings.Secure.getString(mContentResolver,
-                Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME);
-        setAppSourceName(appSourceName, /*updateContentResolver=*/ false);
+        String appSourceName =
+                Settings.Secure.getString(
+                        mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME);
+        setAppSourceName(appSourceName, /* updateContentResolver= */ false);
     }
 
     private void updateBroadcastInfoFromBroadcastMetadata(
@@ -474,12 +542,12 @@
         }
         BluetoothLeAudioContentMetadata contentMetadata = subgroup.get(0).getContentMetadata();
         setProgramInfo(contentMetadata.getProgramInfo());
-        setAppSourceName(getAppSourceName(), /*updateContentResolver=*/ true);
+        setAppSourceName(getAppSourceName(), /* updateContentResolver= */ true);
     }
 
     /**
-     * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system
-     * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}.
+     * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system calls
+     * the corresponding callback {@link BluetoothLeBroadcast.Callback}.
      */
     public void stopLatestBroadcast() {
         stopBroadcast(mBroadcastId);
@@ -511,7 +579,8 @@
         }
         String programInfo = getProgramInfo();
         if (DEBUG) {
-            Log.d(TAG,
+            Log.d(
+                    TAG,
                     "updateBroadcast: language = " + language + " ,programInfo = " + programInfo);
         }
         mNewAppSourceName = appSourceName;
@@ -519,50 +588,79 @@
         mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
     }
 
-    public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
-            @NonNull BluetoothLeBroadcast.Callback callback) {
-        if (mServiceBroadcast == null) {
-            Log.d(TAG, "The BluetoothLeBroadcast is null.");
-            return;
-        }
-
-        mServiceBroadcast.registerCallback(executor, callback);
-    }
-
     /**
-     * Register Broadcast Assistant Callbacks to track it's state and receivers
+     * Register Broadcast Callbacks to track its state and receivers
      *
      * @param executor Executor object for callback
      * @param callback Callback object to be registered
      */
-    public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerServiceCallBack(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BluetoothLeBroadcast.Callback callback) {
+        if (mServiceBroadcast == null) {
+            Log.d(TAG, "registerServiceCallBack failed, the BluetoothLeBroadcast is null.");
+            mCachedBroadcastCallbackExecutorMap.putIfAbsent(callback, executor);
+            return;
+        }
+
+        try {
+            mServiceBroadcast.registerCallback(executor, callback);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage());
+        }
+    }
+
+    /**
+     * Register Broadcast Assistant Callbacks to track its state and receivers
+     *
+     * @param executor Executor object for callback
+     * @param callback Callback object to be registered
+     */
+    private void registerBroadcastAssistantCallback(
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
         if (mServiceBroadcastAssistant == null) {
-            Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null.");
+            Log.d(
+                    TAG,
+                    "registerBroadcastAssistantCallback failed, "
+                            + "the BluetoothLeBroadcastAssistant is null.");
             return;
         }
 
         mServiceBroadcastAssistant.registerCallback(executor, callback);
     }
 
-    public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
-        if (mServiceBroadcast == null) {
-            Log.d(TAG, "The BluetoothLeBroadcast is null.");
-            return;
-        }
-
-        mServiceBroadcast.unregisterCallback(callback);
-    }
-
     /**
-     * Unregister previousely registered Broadcast Assistant Callbacks
+     * Unregister previously registered Broadcast Callbacks
      *
      * @param callback Callback object to be unregistered
      */
-    public void unregisterBroadcastAssistantCallback(
+    public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
+        mCachedBroadcastCallbackExecutorMap.remove(callback);
+        if (mServiceBroadcast == null) {
+            Log.d(TAG, "unregisterServiceCallBack failed, the BluetoothLeBroadcast is null.");
+            return;
+        }
+
+        try {
+            mServiceBroadcast.unregisterCallback(callback);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage());
+        }
+    }
+
+    /**
+     * Unregister previously registered Broadcast Assistant Callbacks
+     *
+     * @param callback Callback object to be unregistered
+     */
+    private void unregisterBroadcastAssistantCallback(
             @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
         if (mServiceBroadcastAssistant == null) {
-            Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null.");
+            Log.d(
+                    TAG,
+                    "unregisterBroadcastAssistantCallback, "
+                            + "the BluetoothLeBroadcastAssistant is null.");
             return;
         }
 
@@ -570,8 +668,8 @@
     }
 
     private void buildContentMetadata(String language, String programInfo) {
-        mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo(
-                programInfo).build();
+        mBluetoothLeAudioContentMetadata =
+                mBuilder.setLanguage(language).setProgramInfo(programInfo).build();
     }
 
     public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() {
@@ -600,9 +698,7 @@
         return true;
     }
 
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection.
-     */
+    /** Not supported since LE Audio Broadcasts do not establish a connection. */
     public int getConnectionStatus(BluetoothDevice device) {
         if (mServiceBroadcast == null) {
             return BluetoothProfile.STATE_DISCONNECTED;
@@ -611,9 +707,7 @@
         return mServiceBroadcast.getConnectionState(device);
     }
 
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection.
-     */
+    /** Not supported since LE Audio Broadcasts do not establish a connection. */
     public List<BluetoothDevice> getConnectedDevices() {
         if (mServiceBroadcast == null) {
             return new ArrayList<BluetoothDevice>(0);
@@ -622,8 +716,8 @@
         return mServiceBroadcast.getConnectedDevices();
     }
 
-    public @NonNull
-    List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
+    /** Get all broadcast metadata. */
+    public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
         if (mServiceBroadcast == null) {
             Log.d(TAG, "The BluetoothLeBroadcast is null.");
             return Collections.emptyList();
@@ -640,16 +734,14 @@
         return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty();
     }
 
-    /**
-     * Service does not provide method to get/set policy.
-     */
+    /** Service does not provide method to get/set policy. */
     public int getConnectionPolicy(BluetoothDevice device) {
         return CONNECTION_POLICY_FORBIDDEN;
     }
 
     /**
-     * Service does not provide "setEnabled" method. Please use {@link #startBroadcast},
-     * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)}
+     * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, {@link
+     * #stopBroadcast()} or {@link #updateBroadcast(String, String)}
      */
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
         return false;
@@ -683,9 +775,8 @@
         }
         if (mServiceBroadcast != null) {
             try {
-                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
-                        BluetoothProfile.LE_AUDIO_BROADCAST,
-                        mServiceBroadcast);
+                BluetoothAdapter.getDefaultAdapter()
+                        .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST, mServiceBroadcast);
                 mServiceBroadcast = null;
             } catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up LeAudio proxy", t);
@@ -694,13 +785,13 @@
     }
 
     private String getDefaultValueOfProgramInfo() {
-        //set the default value;
+        // set the default value;
         int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX);
         return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix;
     }
 
     private byte[] getDefaultValueOfBroadcastCode() {
-        //set the default value;
+        // set the default value;
         return generateRandomPassword().getBytes(StandardCharsets.UTF_8);
     }
 
@@ -708,14 +799,14 @@
         if (DEBUG) {
             Log.d(TAG, "resetCacheInfo:");
         }
-        setAppSourceName("", /*updateContentResolver=*/ true);
+        setAppSourceName("", /* updateContentResolver= */ true);
         mBluetoothLeBroadcastMetadata = null;
         mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
     }
 
     private String generateRandomPassword() {
         String randomUUID = UUID.randomUUID().toString();
-        //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+        // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
         return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
     }
 
@@ -752,5 +843,4 @@
             }
         }
     }
-
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index bb103b8..34008ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -39,13 +39,14 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
-
 /**
- * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app
- * and the functionality of the local {@link BluetoothLeBroadcastAssistant}.
- * Use the {@link BluetoothLeBroadcastAssistant.Callback} to get the result callback.
+ * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app and the
+ * functionality of the local {@link BluetoothLeBroadcastAssistant}. Use the {@link
+ * BluetoothLeBroadcastAssistant.Callback} to get the result callback.
  */
 public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile {
     private static final String TAG = "LocalBluetoothLeBroadcastAssistant";
@@ -62,58 +63,76 @@
     private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
     private BluetoothLeBroadcastMetadata.Builder mBuilder;
     private boolean mIsProfileReady;
+    // Cached assistant callbacks being register before service is connected.
+    private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCachedCallbackExecutorMap =
+            new ConcurrentHashMap<>();
 
-    private final ServiceListener mServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (DEBUG) {
-                Log.d(TAG, "Bluetooth service connected");
-            }
-            mService = (BluetoothLeBroadcastAssistant) proxy;
-            // We just bound to the service, so refresh the UI for any connected LeAudio devices.
-            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-            while (!deviceList.isEmpty()) {
-                BluetoothDevice nextDevice = deviceList.remove(0);
-                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
-                // we may add a new device here, but generally this should not happen
-                if (device == null) {
+    private final ServiceListener mServiceListener =
+            new ServiceListener() {
+                @Override
+                public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     if (DEBUG) {
-                        Log.d(TAG, "LocalBluetoothLeBroadcastAssistant found new device: "
-                                + nextDevice);
+                        Log.d(TAG, "Bluetooth service connected");
                     }
-                    device = mDeviceManager.addDevice(nextDevice);
+                    mService = (BluetoothLeBroadcastAssistant) proxy;
+                    // We just bound to the service, so refresh the UI for any connected LeAudio
+                    // devices.
+                    List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+                    while (!deviceList.isEmpty()) {
+                        BluetoothDevice nextDevice = deviceList.remove(0);
+                        CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                        // we may add a new device here, but generally this should not happen
+                        if (device == null) {
+                            if (DEBUG) {
+                                Log.d(
+                                        TAG,
+                                        "LocalBluetoothLeBroadcastAssistant found new device: "
+                                                + nextDevice);
+                            }
+                            device = mDeviceManager.addDevice(nextDevice);
+                        }
+                        device.onProfileStateChanged(
+                                LocalBluetoothLeBroadcastAssistant.this,
+                                BluetoothProfile.STATE_CONNECTED);
+                        device.refresh();
+                    }
+
+                    mProfileManager.callServiceConnectedListeners();
+                    mIsProfileReady = true;
+                    if (DEBUG) {
+                        Log.d(
+                                TAG,
+                                "onServiceConnected, register mCachedCallbackExecutorMap = "
+                                        + mCachedCallbackExecutorMap);
+                    }
+                    mCachedCallbackExecutorMap.forEach(
+                            (callback, executor) -> registerServiceCallBack(executor, callback));
                 }
-                device.onProfileStateChanged(LocalBluetoothLeBroadcastAssistant.this,
-                        BluetoothProfile.STATE_CONNECTED);
-                device.refresh();
-            }
 
-            mProfileManager.callServiceConnectedListeners();
-            mIsProfileReady = true;
-        }
+                @Override
+                public void onServiceDisconnected(int profile) {
+                    if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) {
+                        Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT");
+                        return;
+                    }
+                    if (DEBUG) {
+                        Log.d(TAG, "Bluetooth service disconnected");
+                    }
+                    mProfileManager.callServiceDisconnectedListeners();
+                    mIsProfileReady = false;
+                    mCachedCallbackExecutorMap.clear();
+                }
+            };
 
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) {
-                Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT");
-                return;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "Bluetooth service disconnected");
-            }
-            mProfileManager.callServiceDisconnectedListeners();
-            mIsProfileReady = false;
-        }
-    };
-
-    public LocalBluetoothLeBroadcastAssistant(Context context,
+    public LocalBluetoothLeBroadcastAssistant(
+            Context context,
             CachedBluetoothDeviceManager deviceManager,
             LocalBluetoothProfileManager profileManager) {
         mProfileManager = profileManager;
         mDeviceManager = deviceManager;
-        BluetoothAdapter.getDefaultAdapter().
-                getProfileProxy(context, mServiceListener,
-                        BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+        BluetoothAdapter.getDefaultAdapter()
+                .getProfileProxy(
+                        context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
         mBuilder = new BluetoothLeBroadcastMetadata.Builder();
     }
 
@@ -123,11 +142,11 @@
      * @param sink Broadcast Sink to which the Broadcast Source should be added
      * @param metadata Broadcast Source metadata to be added to the Broadcast Sink
      * @param isGroupOp {@code true} if Application wants to perform this operation for all
-     *                  coordinated set members throughout this session. Otherwise, caller
-     *                  would have to add, modify, and remove individual set members.
+     *     coordinated set members throughout this session. Otherwise, caller would have to add,
+     *     modify, and remove individual set members.
      */
-    public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata,
-            boolean isGroupOp) {
+    public void addSource(
+            BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) {
         if (mService == null) {
             Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
             return;
@@ -140,36 +159,55 @@
      * the qr code string.
      *
      * @param sink Broadcast Sink to which the Broadcast Source should be added
-     * @param sourceAddressType hardware MAC Address of the device. See
-     *                          {@link BluetoothDevice.AddressType}.
+     * @param sourceAddressType hardware MAC Address of the device. See {@link
+     *     BluetoothDevice.AddressType}.
      * @param presentationDelayMicros presentation delay of this Broadcast Source in microseconds.
      * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source.
      * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source.
-     * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source,
-     *                       {@link BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if
-     *                       unknown.
+     * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, {@link
+     *     BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if unknown.
      * @param isEncrypted whether the Broadcast Source is encrypted.
      * @param broadcastCode Broadcast Code for this Broadcast Source, null if code is not required.
      * @param sourceDevice source advertiser address.
      * @param isGroupOp {@code true} if Application wants to perform this operation for all
-     *                  coordinated set members throughout this session. Otherwise, caller
-     *                  would have to add, modify, and remove individual set members.
+     *     coordinated set members throughout this session. Otherwise, caller would have to add,
+     *     modify, and remove individual set members.
      */
-    public void addSource(@NonNull BluetoothDevice sink, int sourceAddressType,
-            int presentationDelayMicros, int sourceAdvertisingSid, int broadcastId,
-            int paSyncInterval, boolean isEncrypted, byte[] broadcastCode,
-            BluetoothDevice sourceDevice, boolean isGroupOp) {
+    public void addSource(
+            @NonNull BluetoothDevice sink,
+            int sourceAddressType,
+            int presentationDelayMicros,
+            int sourceAdvertisingSid,
+            int broadcastId,
+            int paSyncInterval,
+            boolean isEncrypted,
+            byte[] broadcastCode,
+            BluetoothDevice sourceDevice,
+            boolean isGroupOp) {
         if (DEBUG) {
             Log.d(TAG, "addSource()");
         }
-        buildMetadata(sourceAddressType, presentationDelayMicros, sourceAdvertisingSid, broadcastId,
-                paSyncInterval, isEncrypted, broadcastCode, sourceDevice);
+        buildMetadata(
+                sourceAddressType,
+                presentationDelayMicros,
+                sourceAdvertisingSid,
+                broadcastId,
+                paSyncInterval,
+                isEncrypted,
+                broadcastCode,
+                sourceDevice);
         addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp);
     }
 
-    private void buildMetadata(int sourceAddressType, int presentationDelayMicros,
-            int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted,
-            byte[] broadcastCode, BluetoothDevice sourceDevice) {
+    private void buildMetadata(
+            int sourceAddressType,
+            int presentationDelayMicros,
+            int sourceAdvertisingSid,
+            int broadcastId,
+            int paSyncInterval,
+            boolean isEncrypted,
+            byte[] broadcastCode,
+            BluetoothDevice sourceDevice) {
         mBluetoothLeBroadcastMetadata =
                 mBuilder.setSourceDevice(sourceDevice, sourceAddressType)
                         .setSourceAdvertisingSid(sourceAdvertisingSid)
@@ -223,10 +261,10 @@
     /**
      * Stops an ongoing search for nearby Broadcast Sources.
      *
-     * On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be
-     * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}.
-     * On failure, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be
-     * called with reason code
+     * <p>On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be
+     * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. On failure,
+     * {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be called with
+     * reason code
      *
      * @throws IllegalStateException if callback was not registered
      */
@@ -245,8 +283,8 @@
      * Get information about all Broadcast Sources that a Broadcast Sink knows about.
      *
      * @param sink Broadcast Sink from which to get all Broadcast Sources
-     * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState}
-     *         stored in the Broadcast Sink
+     * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} stored
+     *     in the Broadcast Sink
      * @throws NullPointerException when <var>sink</var> is null
      */
     public @NonNull List<BluetoothLeBroadcastReceiveState> getAllSources(
@@ -261,24 +299,50 @@
         return mService.getAllSources(sink);
     }
 
-    public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
+    /**
+     * Register Broadcast Assistant Callbacks to track its state and receivers
+     *
+     * @param executor Executor object for callback
+     * @param callback Callback object to be registered
+     */
+    public void registerServiceCallBack(
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
         if (mService == null) {
-            Log.d(TAG, "The BluetoothLeBroadcast is null.");
+            Log.d(
+                    TAG,
+                    "registerServiceCallBack failed, the BluetoothLeBroadcastAssistant is null.");
+            mCachedCallbackExecutorMap.putIfAbsent(callback, executor);
             return;
         }
 
-        mService.registerCallback(executor, callback);
+        try {
+            mService.registerCallback(executor, callback);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage());
+        }
     }
 
+    /**
+     * Unregister previously registered Broadcast Assistant Callbacks
+     *
+     * @param callback Callback object to be unregistered
+     */
     public void unregisterServiceCallBack(
             @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
+        mCachedCallbackExecutorMap.remove(callback);
         if (mService == null) {
-            Log.d(TAG, "The BluetoothLeBroadcast is null.");
+            Log.d(
+                    TAG,
+                    "unregisterServiceCallBack failed, the BluetoothLeBroadcastAssistant is null.");
             return;
         }
 
-        mService.unregisterCallback(callback);
+        try {
+            mService.unregisterCallback(callback);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage());
+        }
     }
 
     public boolean isProfileReady() {
@@ -310,9 +374,11 @@
             return new ArrayList<BluetoothDevice>(0);
         }
         return mService.getDevicesMatchingConnectionStates(
-                new int[]{BluetoothProfile.STATE_CONNECTED,
-                        BluetoothProfile.STATE_CONNECTING,
-                        BluetoothProfile.STATE_DISCONNECTING});
+                new int[] {
+                    BluetoothProfile.STATE_CONNECTED,
+                    BluetoothProfile.STATE_CONNECTING,
+                    BluetoothProfile.STATE_DISCONNECTING
+                });
     }
 
     public boolean isEnabled(BluetoothDevice device) {
@@ -373,9 +439,8 @@
         }
         if (mService != null) {
             try {
-                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
-                        BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
-                        mService);
+                BluetoothAdapter.getDefaultAdapter()
+                        .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, mService);
                 mService = null;
             } catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up LeAudio proxy", t);
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 009f8bb..e538e09 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -175,11 +175,7 @@
     canOverflow: Boolean,
 ): T {
     val state = layoutImpl.state.transitionState
-    if (
-        state !is TransitionState.Transition ||
-            state.fromScene == state.toScene ||
-            !layoutImpl.isTransitionReady(state)
-    ) {
+    if (state !is TransitionState.Transition || !layoutImpl.isTransitionReady(state)) {
         return sharedValue.value
     }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 199832b..de69c37 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -47,12 +47,6 @@
     when (state) {
         is TransitionState.Idle -> animate(layoutImpl, target)
         is TransitionState.Transition -> {
-            if (state.toScene == state.fromScene) {
-                // Same as idle.
-                animate(layoutImpl, target)
-                return
-            }
-
             // A transition is currently running: first check whether `transition.toScene` or
             // `transition.fromScene` is the same as our target scene, in which case the transition
             // can be accelerated or reversed to end up in the target state.
@@ -153,13 +147,13 @@
 }
 
 private class OneOffTransition(
-    override val fromScene: SceneKey,
-    override val toScene: SceneKey,
+    fromScene: SceneKey,
+    toScene: SceneKey,
     override val currentScene: SceneKey,
     override val isInitiatedByUserInput: Boolean,
     override val isUserInputOngoing: Boolean,
     private val animatable: Animatable<Float, AnimationVector1D>,
-) : TransitionState.Transition {
+) : TransitionState.Transition(fromScene, toScene) {
     override val progress: Float
         get() = animatable.value
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 31604a6..431a8ae 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -288,7 +288,6 @@
     // Always draw the element if there is no ongoing transition or if the element is not shared.
     if (
         state !is TransitionState.Transition ||
-            state.fromScene == state.toScene ||
             !layoutImpl.isTransitionReady(state) ||
             state.fromScene !in element.sceneValues ||
             state.toScene !in element.sceneValues
@@ -374,7 +373,7 @@
 ): Boolean {
     val state = layoutImpl.state.transitionState
 
-    if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+    if (state !is TransitionState.Transition) {
         return true
     }
 
@@ -611,7 +610,7 @@
     val state = layoutImpl.state.transitionState
 
     // There is no ongoing transition.
-    if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+    if (state !is TransitionState.Transition) {
         // Even if this element SceneTransitionLayout is not animated, the layout itself might be
         // animated (e.g. by another parent SceneTransitionLayout), in which case this element still
         // need to participate in the layout phase.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index fa385d0..7029da2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -131,10 +131,6 @@
 
     val fromScene = (transitionState as TransitionState.Transition).fromScene
     val toScene = transitionState.toScene
-    if (fromScene == toScene) {
-        check(fromScene == scene)
-        return true
-    }
 
     val fromReady = layoutImpl.isSceneReady(fromScene)
     val toReady = layoutImpl.isSceneReady(toScene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index 1b79dbd..983cff8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -73,17 +73,13 @@
             when (val state = transitionState) {
                 is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene)
                 is TransitionState.Transition -> {
-                    if (state.fromScene == state.toScene) {
-                        ObservableTransitionState.Idle(state.currentScene)
-                    } else {
-                        ObservableTransitionState.Transition(
-                            fromScene = state.fromScene,
-                            toScene = state.toScene,
-                            progress = snapshotFlow { state.progress },
-                            isInitiatedByUserInput = state.isInitiatedByUserInput,
-                            isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
-                        )
-                    }
+                    ObservableTransitionState.Transition(
+                        fromScene = state.fromScene,
+                        toScene = state.toScene,
+                        progress = snapshotFlow { state.progress },
+                        isInitiatedByUserInput = state.isInitiatedByUserInput,
+                        isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
+                    )
                 }
             }
         }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 03f37d0..91decf4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -49,8 +49,12 @@
             layoutImpl.state.transitionState = value
         }
 
-    internal var swipeTransition: SwipeTransition = SwipeTransition(currentScene, currentScene, 1f)
-        private set
+    private var _swipeTransition: SwipeTransition? = null
+    internal var swipeTransition: SwipeTransition
+        get() = _swipeTransition ?: error("SwipeTransition needs to be initialized")
+        set(value) {
+            _swipeTransition = value
+        }
 
     private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
         if (isDrivingTransition || force) transitionState = newTransition
@@ -61,7 +65,7 @@
         get() = layoutImpl.scene(transitionState.currentScene)
 
     internal val isDrivingTransition
-        get() = transitionState == swipeTransition
+        get() = transitionState == _swipeTransition
 
     /**
      * The velocity threshold at which the intent of the user is to swipe up or down. It is the same
@@ -82,12 +86,15 @@
     private var actionDownOrRight: UserAction? = null
     private var actionUpOrLeftNoEdge: UserAction? = null
     private var actionDownOrRightNoEdge: UserAction? = null
+    private var upOrLeftScene: SceneKey? = null
+    private var downOrRightScene: SceneKey? = null
 
     internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?, overSlop: Float) {
         if (isDrivingTransition) {
             // This [transition] was already driving the animation: simply take over it.
             // Stop animating and start from where the current offset.
             swipeTransition.cancelOffsetAnimation()
+            updateTargetScenes(swipeTransition._fromScene)
             return
         }
 
@@ -105,11 +112,8 @@
         val fromScene = currentScene
         setCurrentActions(fromScene, startedPosition, pointersDown)
 
-        if (fromScene.upOrLeft() == null && fromScene.downOrRight() == null) {
-            return
-        }
-
-        val (targetScene, distance) = fromScene.findTargetSceneAndDistance(overSlop)
+        val (targetScene, distance) =
+            findTargetSceneAndDistance(fromScene, overSlop, updateScenes = true) ?: return
 
         updateTransition(SwipeTransition(fromScene, targetScene, distance), force = true)
     }
@@ -179,16 +183,21 @@
 
         val (fromScene, acceleratedOffset) =
             computeFromSceneConsideringAcceleratedSwipe(swipeTransition)
+
+        val isNewFromScene = fromScene.key != swipeTransition.fromScene
+        val (targetScene, distance) =
+            findTargetSceneAndDistance(
+                fromScene,
+                swipeTransition.dragOffset,
+                updateScenes = isNewFromScene,
+            )
+                ?: run {
+                    onDragStopped(delta, true)
+                    return
+                }
         swipeTransition.dragOffset += acceleratedOffset
 
-        // Compute the target scene depending on the current offset.
-        val (targetScene, distance) =
-            fromScene.findTargetSceneAndDistance(swipeTransition.dragOffset)
-
-        // TODO(b/290184746): support long scroll A => B => C? especially for non fullscreen scenes
-        if (
-            fromScene.key != swipeTransition.fromScene || targetScene.key != swipeTransition.toScene
-        ) {
+        if (isNewFromScene || targetScene.key != swipeTransition.toScene) {
             updateTransition(
                 SwipeTransition(fromScene, targetScene, distance).apply {
                     this.dragOffset = swipeTransition.dragOffset
@@ -197,6 +206,11 @@
         }
     }
 
+    private fun updateTargetScenes(fromScene: Scene) {
+        upOrLeftScene = fromScene.upOrLeft()
+        downOrRightScene = fromScene.downOrRight()
+    }
+
     /**
      * Change fromScene in the case where the user quickly swiped multiple times in the same
      * direction to accelerate the transition from A => B then B => C.
@@ -214,37 +228,71 @@
         val absoluteDistance = swipeTransition.distance.absoluteValue
 
         // If the swipe was not committed, don't do anything.
-        if (fromScene == toScene || swipeTransition._currentScene != toScene) {
+        if (swipeTransition._currentScene != toScene) {
             return Pair(fromScene, 0f)
         }
 
         // If the offset is past the distance then let's change fromScene so that the user can swipe
         // to the next screen or go back to the previous one.
         val offset = swipeTransition.dragOffset
-        return if (offset <= -absoluteDistance && fromScene.upOrLeft() == toScene.key) {
+        return if (offset <= -absoluteDistance && upOrLeftScene == toScene.key) {
             Pair(toScene, absoluteDistance)
-        } else if (offset >= absoluteDistance && fromScene.downOrRight() == toScene.key) {
+        } else if (offset >= absoluteDistance && downOrRightScene == toScene.key) {
             Pair(toScene, -absoluteDistance)
         } else {
             Pair(fromScene, 0f)
         }
     }
 
-    // TODO(b/290184746): there are two bugs here:
-    // 1. if both upOrLeft and downOrRight become `null` during a transition this will crash
-    // 2. if one of them changes during a transition, the transition will jump cut to the new target
-    private inline fun Scene.findTargetSceneAndDistance(
-        directionOffset: Float
-    ): Pair<Scene, Float> {
-        val upOrLeft = upOrLeft()
-        val downOrRight = downOrRight()
-        val absoluteDistance = getAbsoluteDistance()
+    /**
+     * Returns the target scene and distance from [fromScene] in the direction [directionOffset].
+     *
+     * @param fromScene the scene from which we look for the target
+     * @param directionOffset signed float that indicates the direction. Positive is down or right
+     *   negative is up or left.
+     * @param updateScenes whether the target scenes should be updated to the current values held in
+     *   the Scenes map. Usually we don't want to update them while doing a drag, because this could
+     *   change the target scene (jump cutting) to a different scene, when some system state changed
+     *   the targets the background. However, an update is needed any time we calculate the targets
+     *   for a new fromScene.
+     * @return null when there are no targets in either direction. If one direction is null and you
+     *   drag into the null direction this function will return the opposite direction, assuming
+     *   that the users intention is to start the drag into the other direction eventually. If
+     *   [directionOffset] is 0f and both direction are available, it will default to
+     *   [upOrLeftScene].
+     */
+    private inline fun findTargetSceneAndDistance(
+        fromScene: Scene,
+        directionOffset: Float,
+        updateScenes: Boolean,
+    ): Pair<Scene, Float>? {
+        if (updateScenes) updateTargetScenes(fromScene)
+        val absoluteDistance = fromScene.getAbsoluteDistance()
 
         // Compute the target scene depending on the current offset.
-        return if ((directionOffset < 0f && upOrLeft != null) || downOrRight == null) {
-            Pair(layoutImpl.scene(upOrLeft!!), -absoluteDistance)
-        } else {
-            Pair(layoutImpl.scene(downOrRight), absoluteDistance)
+        return when {
+            upOrLeftScene == null && downOrRightScene == null -> null
+            (directionOffset < 0f && upOrLeftScene != null) || downOrRightScene == null ->
+                Pair(layoutImpl.scene(upOrLeftScene!!), -absoluteDistance)
+            else -> Pair(layoutImpl.scene(downOrRightScene!!), absoluteDistance)
+        }
+    }
+
+    /**
+     * A strict version of [findTargetSceneAndDistance] that will return null when there is no Scene
+     * in [directionOffset] direction
+     */
+    private inline fun findTargetSceneAndDistanceStrict(
+        fromScene: Scene,
+        directionOffset: Float,
+    ): Pair<Scene, Float>? {
+        val absoluteDistance = fromScene.getAbsoluteDistance()
+        return when {
+            directionOffset > 0f ->
+                upOrLeftScene?.let { Pair(layoutImpl.scene(it), -absoluteDistance) }
+            directionOffset < 0f ->
+                downOrRightScene?.let { Pair(layoutImpl.scene(it), absoluteDistance) }
+            else -> null
         }
     }
 
@@ -311,20 +359,21 @@
             val startFromIdlePosition = swipeTransition.dragOffset == 0f
 
             if (startFromIdlePosition) {
-                // If there is a next scene, we start the overscroll animation.
-                val (targetScene, distance) = fromScene.findTargetSceneAndDistance(velocity)
-                val isValidTarget = distance != 0f && targetScene.key != fromScene.key
-                if (isValidTarget) {
-                    updateTransition(
-                        SwipeTransition(fromScene, targetScene, distance).apply {
-                            _currentScene = swipeTransition._currentScene
+                // If there is a target scene, we start the overscroll animation.
+                val (targetScene, distance) =
+                    findTargetSceneAndDistanceStrict(fromScene, velocity)
+                        ?: run {
+                            // We will not animate
+                            transitionState = TransitionState.Idle(fromScene.key)
+                            return
                         }
-                    )
-                    animateTo(targetScene = fromScene, targetOffset = 0f)
-                } else {
-                    // We will not animate
-                    transitionState = TransitionState.Idle(fromScene.key)
-                }
+
+                updateTransition(
+                    SwipeTransition(fromScene, targetScene, distance).apply {
+                        _currentScene = swipeTransition._currentScene
+                    }
+                )
+                animateTo(targetScene = fromScene, targetOffset = 0f)
             } else {
                 // We were between two scenes: animate to the initial scene.
                 animateTo(targetScene = fromScene, targetOffset = 0f)
@@ -410,15 +459,11 @@
          * above or to the left of [toScene].
          */
         val distance: Float
-    ) : TransitionState.Transition {
+    ) : TransitionState.Transition(_fromScene.key, _toScene.key) {
         var _currentScene by mutableStateOf(_fromScene)
         override val currentScene: SceneKey
             get() = _currentScene.key
 
-        override val fromScene: SceneKey = _fromScene.key
-
-        override val toScene: SceneKey = _toScene.key
-
         override val progress: Float
             get() {
                 val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
@@ -494,9 +539,9 @@
 }
 
 internal class SceneNestedScrollHandler(
-        private val gestureHandler: SceneGestureHandler,
-        private val topOrLeftBehavior: NestedScrollBehavior,
-        private val bottomOrRightBehavior: NestedScrollBehavior,
+    private val gestureHandler: SceneGestureHandler,
+    private val topOrLeftBehavior: NestedScrollBehavior,
+    private val bottomOrRightBehavior: NestedScrollBehavior,
 ) : NestedScrollHandler {
     override val connection: PriorityNestedScrollConnection = nestedScrollConnection()
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 02ddccb..00e33e2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -172,7 +172,7 @@
                     val width: Int
                     val height: Int
                     val state = state.transitionState
-                    if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+                    if (state !is TransitionState.Transition) {
                         width = placeable.width
                         height = placeable.height
                     } else {
@@ -232,10 +232,7 @@
                                         is TransitionState.Idle -> drawContent()
                                         is TransitionState.Transition -> {
                                             // Don't draw scenes that are not ready yet.
-                                            if (
-                                                readyScenes.containsKey(key) ||
-                                                    state.fromScene == state.toScene
-                                            ) {
+                                            if (readyScenes.containsKey(key)) {
                                                 drawContent()
                                             }
                                         }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index f48e914..62372558 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -39,11 +39,6 @@
     fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
         val transition = transitionState as? TransitionState.Transition ?: return false
 
-        // TODO(b/310915136): Remove this check.
-        if (transition.fromScene == transition.toScene) {
-            return false
-        }
-
         return (from == null || transition.fromScene == from) &&
             (to == null || transition.toScene == to)
     }
@@ -71,32 +66,30 @@
     /** No transition/animation is currently running. */
     data class Idle(override val currentScene: SceneKey) : TransitionState
 
-    /**
-     * There is a transition animating between two scenes.
-     *
-     * Important note: [fromScene] and [toScene] might be the same, in which case this [Transition]
-     * should be treated the same as [Idle]. This is designed on purpose so that a [Transition] can
-     * be started without knowing in advance where it is transitioning to, making the logic of
-     * [swipeToScene] easier to reason about.
-     */
-    interface Transition : TransitionState {
-        /** The scene this transition is starting from. */
-        val fromScene: SceneKey
+    /** There is a transition animating between two scenes. */
+    abstract class Transition(
+        /** The scene this transition is starting from. Can't be the same as toScene */
+        val fromScene: SceneKey,
 
-        /** The scene this transition is going to. */
+        /** The scene this transition is going to. Can't be the same as fromScene */
         val toScene: SceneKey
+    ) : TransitionState {
+
+        init {
+            check(fromScene != toScene)
+        }
 
         /**
          * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
          * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
          * when flinging quickly during a swipe gesture.
          */
-        val progress: Float
+        abstract val progress: Float
 
         /** Whether the transition was triggered by user input rather than being programmatic. */
-        val isInitiatedByUserInput: Boolean
+        abstract val isInitiatedByUserInput: Boolean
 
         /** Whether user input is currently driving the transition. */
-        val isUserInputOngoing: Boolean
+        abstract val isUserInputOngoing: Boolean
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index 34afc4c..e6224df 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -58,16 +58,22 @@
         private val layoutState: SceneTransitionLayoutState =
             SceneTransitionLayoutState(internalCurrentScene)
 
+        val mutableUserActionsA: MutableMap<UserAction, SceneKey> =
+            mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
+
+        val mutableUserActionsB: MutableMap<UserAction, SceneKey> =
+            mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
+
         private val scenesBuilder: SceneTransitionLayoutScope.() -> Unit = {
             scene(
                 key = SceneA,
-                userActions = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneC),
+                userActions = mutableUserActionsA,
             ) {
                 Text("SceneA")
             }
             scene(
                 key = SceneB,
-                userActions = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA),
+                userActions = mutableUserActionsB,
             ) {
                 Text("SceneB")
             }
@@ -412,6 +418,70 @@
     }
 
     @Test
+    fun onAccelaratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest {
+        draggable.onDragStarted()
+        draggable.onDelta(up(0.2f))
+
+        draggable.onDelta(up(0.2f))
+        draggable.onDragStopped(velocity = -velocityThreshold)
+        assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
+
+        mutableUserActionsA.remove(Swipe.Up)
+        mutableUserActionsA.remove(Swipe.Down)
+        mutableUserActionsB.remove(Swipe.Up)
+        mutableUserActionsB.remove(Swipe.Down)
+
+        // start accelaratedScroll and scroll over to B -> null
+        draggable.onDragStarted()
+        draggable.onDelta(up(0.5f))
+        draggable.onDelta(up(0.5f))
+
+        // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may
+        // still be called. Make sure that they don't crash or change the scene
+        draggable.onDelta(up(0.5f))
+        draggable.onDragStopped(0f)
+
+        advanceUntilIdle()
+        assertIdle(SceneB)
+
+        // These events can still come in after the animation has settled
+        draggable.onDelta(up(0.5f))
+        draggable.onDragStopped(0f)
+        assertIdle(SceneB)
+    }
+
+    @Test
+    fun onDragTargetsChanged_targetStaysTheSame() = runGestureTest {
+        draggable.onDragStarted(up(0.1f))
+        assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f)
+
+        mutableUserActionsA[Swipe.Up] = SceneC
+        draggable.onDelta(up(0.1f))
+        // target stays B even though UserActions changed
+        assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.2f)
+        draggable.onDragStopped(down(0.1f))
+        advanceUntilIdle()
+
+        // now target changed to C for new drag
+        draggable.onDragStarted(up(0.1f))
+        assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.1f)
+    }
+
+    @Test
+    fun onDragTargetsChanged_targetsChangeWhenStartingNewDrag() = runGestureTest {
+        draggable.onDragStarted(up(0.1f))
+        assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f)
+
+        mutableUserActionsA[Swipe.Up] = SceneC
+        draggable.onDelta(up(0.1f))
+        draggable.onDragStopped(down(0.1f))
+
+        // now target changed to C for new drag that started before previous drag settled to Idle
+        draggable.onDragStarted(up(0.1f))
+        assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.3f)
+    }
+
+    @Test
     fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest {
         draggable.onDragStarted()
         assertTransition(currentScene = SceneA)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 94c51ca..eeda8d46 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -39,18 +39,6 @@
     }
 
     @Test
-    fun isTransitioningTo_fromSceneEqualToToScene() {
-        val state = SceneTransitionLayoutState(TestScenes.SceneA)
-        state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneA)
-
-        assertThat(state.isTransitioning()).isFalse()
-        assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
-        assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse()
-        assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB))
-            .isFalse()
-    }
-
-    @Test
     fun isTransitioningTo_transition() {
         val state = SceneTransitionLayoutState(TestScenes.SceneA)
         state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneB)
@@ -64,10 +52,8 @@
     }
 
     private fun transition(from: SceneKey, to: SceneKey): TransitionState.Transition {
-        return object : TransitionState.Transition {
+        return object : TransitionState.Transition(from, to) {
             override val currentScene: SceneKey = from
-            override val fromScene: SceneKey = from
-            override val toScene: SceneKey = to
             override val progress: Float = 0f
             override val isInitiatedByUserInput: Boolean = false
             override val isUserInputOngoing: Boolean = false
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 45aca17..d6d5b23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -827,25 +827,6 @@
         }
 
     @Test
-    fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() =
-        testScope.runTest {
-            initCollectors()
-            allPreconditionsToRunFaceAuthAreTrue()
-
-            triggerFaceAuth(false)
-
-            authenticationCallback.value.onAuthenticationSucceeded(
-                mock(FaceManager.AuthenticationResult::class.java)
-            )
-
-            assertThat(authenticated()).isTrue()
-
-            keyguardRepository.setKeyguardGoingAway(true)
-
-            assertThat(authenticated()).isFalse()
-        }
-
-    @Test
     fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() =
         testScope.runTest {
             initCollectors()
@@ -906,6 +887,25 @@
         }
 
     @Test
+    fun isAuthenticatedIsResetToFalseWhenKeyguardDoneAnimationsFinished() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            keyguardRepository.keyguardDoneAnimationsFinished()
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
     fun detectDoesNotRunWhenFaceIsNotUsuallyAllowed() =
         testScope.runTest {
             testGatingCheckForDetect {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt
new file mode 100644
index 0000000..e850456
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt
@@ -0,0 +1,40 @@
+package com.android.systemui.keyguard.ui.preview
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardRemotePreviewManagerTest : SysuiTestCase() {
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Test
+    fun onDestroy_clearsReferencesToRenderer() =
+        testScope.runTest {
+            val renderer = mock<KeyguardPreviewRenderer>()
+            val onDestroy: (PreviewLifecycleObserver) -> Unit = {}
+
+            val observer = PreviewLifecycleObserver(this, testDispatcher, renderer, onDestroy)
+
+            // Precondition check.
+            assertThat(observer.renderer).isNotNull()
+            assertThat(observer.onDestroy).isNotNull()
+
+            observer.onDestroy()
+
+            // The verification checks renderer/requestDestruction lambda because they-re
+            // non-singletons which can't leak KeyguardPreviewRenderer.
+            assertThat(observer.renderer).isNull()
+            assertThat(observer.onDestroy).isNull()
+        }
+}
diff --git a/packages/SystemUI/res/drawable/arrow_pointing_down.xml b/packages/SystemUI/res/drawable/arrow_pointing_down.xml
new file mode 100644
index 0000000..be39683
--- /dev/null
+++ b/packages/SystemUI/res/drawable/arrow_pointing_down.xml
@@ -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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml
new file mode 100644
index 0000000..53ad9f1
--- /dev/null
+++ b/packages/SystemUI/res/layout/record_issue_dialog.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:orientation="vertical" >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+        android:text="@string/qs_record_issue_dropdown_header" />
+
+    <Button
+        android:id="@+id/issue_type_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/qs_record_issue_dropdown_prompt"
+        android:lines="1"
+        android:drawableRight="@drawable/arrow_pointing_down"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:focusable="false"
+        android:clickable="true" />
+
+    <!-- Screen Record Switch -->
+    <LinearLayout
+        android:id="@+id/screenrecord_switch_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:layout_width="@dimen/screenrecord_option_icon_size"
+            android:layout_height="@dimen/screenrecord_option_icon_size"
+            android:layout_weight="0"
+            android:src="@drawable/ic_screenrecord"
+            app:tint="?androidprv:attr/materialColorOnSurface"
+            android:layout_gravity="center"
+            android:layout_marginEnd="@dimen/screenrecord_option_padding" />
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_weight="1"
+            android:layout_gravity="fill_vertical"
+            android:gravity="center"
+            android:text="@string/quick_settings_screen_record_label"
+            android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+            android:importantForAccessibility="no"/>
+
+        <Switch
+            android:id="@+id/screenrecord_switch"
+            android:layout_width="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_gravity="fill_vertical"
+            android:layout_weight="0"
+            android:contentDescription="@string/quick_settings_screen_record_label" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 13d2fea..e10925d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -831,6 +831,20 @@
     <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] -->
     <string name="qs_record_issue_stop">Stop</string>
 
+    <!-- QuickSettings: Issue Type Drop down options in Record Issue Start Dialog [CHAR LIMIT=50] -->
+    <string name="qs_record_issue_dropdown_header">What part of your device experience was affected?</string>
+    <!-- QuickSettings: Issue Type Drop down prompt in Record Issue Start Dialog [CHAR LIMIT=30] -->
+    <string name="qs_record_issue_dropdown_prompt">Select issue type</string>
+    <!-- QuickSettings: Screen record switch label in Record Issue Start Dialog [CHAR LIMIT=20] -->
+    <string name="qs_record_issue_dropdown_screenrecord">Screen record</string>
+
+    <!-- QuickSettings: Issue Type Drop down choices list in Record Issue Start Dialog [CHAR LIMIT=30] -->
+    <string-array name="qs_record_issue_types">
+        <item>Performance</item>
+        <item>User Interface</item>
+        <item>Battery</item>
+    </string-array>
+
     <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_onehanded_label">One-handed mode</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b7260f2..57f3b70 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -141,6 +141,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.flags.SystemPropertiesHelper;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.log.SessionTracker;
@@ -1319,6 +1320,7 @@
     private DeviceConfigProxy mDeviceConfig;
     private DozeParameters mDozeParameters;
     private SelectedUserInteractor mSelectedUserInteractor;
+    private KeyguardInteractor mKeyguardInteractor;
 
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -1400,7 +1402,8 @@
             Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
             SystemPropertiesHelper systemPropertiesHelper,
             Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            KeyguardInteractor keyguardInteractor) {
         mContext = context;
         mUserTracker = userTracker;
         mFalsingCollector = falsingCollector;
@@ -1441,6 +1444,7 @@
                 }));
         mDozeParameters = dozeParameters;
         mSelectedUserInteractor = selectedUserInteractor;
+        mKeyguardInteractor = keyguardInteractor;
 
         mStatusBarStateController = statusBarStateController;
         statusBarStateController.addCallback(this);
@@ -2618,6 +2622,7 @@
         setPendingLock(false); // user may have authenticated during the screen off animation
 
         handleHide();
+        mKeyguardInteractor.keyguardDoneAnimationsFinished();
         mUpdateMonitor.clearFingerprintRecognizedWhenKeyguardDone(currentUser);
         Trace.endSection();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 331d892..3925dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -52,6 +52,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
 import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule;
 import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
@@ -154,7 +155,8 @@
             Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
             SystemPropertiesHelper systemPropertiesHelper,
             Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            KeyguardInteractor keyguardInteractor) {
         return new KeyguardViewMediator(
                 context,
                 uiEventLogger,
@@ -199,7 +201,8 @@
                 dreamingToLockscreenTransitionViewModel,
                 systemPropertiesHelper,
                 wmLockscreenVisibilityManager,
-                selectedUserInteractor);
+                selectedUserInteractor,
+                keyguardInteractor);
     }
 
     /** */
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 eceaf6c..4d60dd0 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
@@ -187,7 +187,8 @@
         faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
 
     private val _isAuthRunning = MutableStateFlow(false)
-    override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
+    override val isAuthRunning: StateFlow<Boolean>
+        get() = _isAuthRunning
 
     private val keyguardSessionId: InstanceId?
         get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
@@ -253,13 +254,6 @@
                 )
                 .andAllFlows("canFaceAuthRun", faceAuthLog)
                 .flowOn(backgroundDispatcher)
-                .onEach {
-                    faceAuthLogger.canFaceAuthRunChanged(it)
-                    if (!it) {
-                        // Cancel currently running auth if any of the gating checks are false.
-                        cancel()
-                    }
-                }
                 .stateIn(applicationScope, SharingStarted.Eagerly, false)
 
         // Face detection can run only when lockscreen bypass is enabled
@@ -287,12 +281,9 @@
                 )
                 .andAllFlows("canFaceDetectRun", faceDetectLog)
                 .flowOn(backgroundDispatcher)
-                .onEach {
-                    if (!it) {
-                        cancelDetection()
-                    }
-                }
                 .stateIn(applicationScope, SharingStarted.Eagerly, false)
+        observeFaceAuthGatingChecks()
+        observeFaceDetectGatingChecks()
         observeFaceAuthResettingConditions()
         listenForSchedulingWatchdog()
         processPendingAuthRequests()
@@ -313,14 +304,14 @@
     }
 
     private fun observeFaceAuthResettingConditions() {
-        // Clear auth status when keyguard is going away or when the user is switching or device
-        // starts going to sleep.
+        // Clear auth status when keyguard done animations finished or when the user is switching
+        // or device starts going to sleep.
         merge(
                 powerInteractor.isAsleep,
                 if (featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
                     keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE)
                 } else {
-                    keyguardRepository.isKeyguardGoingAway
+                    keyguardRepository.keyguardDoneAnimationsFinished.map { true }
                 },
                 userRepository.selectedUser.map {
                     it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
@@ -347,6 +338,17 @@
         pendingAuthenticateRequest.value = null
     }
 
+    private fun observeFaceDetectGatingChecks() {
+        canRunDetection
+            .onEach {
+                if (!it) {
+                    cancelDetection()
+                }
+            }
+            .flowOn(mainDispatcher)
+            .launchIn(applicationScope)
+    }
+
     private fun isUdfps() =
         deviceEntryFingerprintAuthRepository.availableFpSensorType.map {
             it == BiometricType.UNDER_DISPLAY_FINGERPRINT
@@ -405,6 +407,20 @@
         )
     }
 
+    private fun observeFaceAuthGatingChecks() {
+        canRunFaceAuth
+            .onEach {
+                faceAuthLogger.canFaceAuthRunChanged(it)
+                if (!it) {
+                    // Cancel currently running auth if any of the gating checks are false.
+                    faceAuthLogger.cancellingFaceAuth()
+                    cancel()
+                }
+            }
+            .flowOn(mainDispatcher)
+            .launchIn(applicationScope)
+    }
+
     private val faceAuthCallback =
         object : FaceManager.AuthenticationCallback() {
             override fun onAuthenticationFailed() {
@@ -539,7 +555,7 @@
                     authenticate(it.uiEvent, it.fallbackToDetection)
                 }
             }
-            .flowOn(backgroundDispatcher)
+            .flowOn(mainDispatcher)
             .launchIn(applicationScope)
     }
 
@@ -635,7 +651,6 @@
     override fun cancel() {
         if (authCancellationSignal == null) return
 
-        faceAuthLogger.cancellingFaceAuth()
         authCancellationSignal?.cancel()
         cancelNotReceivedHandlerJob?.cancel()
         cancelNotReceivedHandlerJob =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 0df7f9b..31ef100 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -192,6 +192,17 @@
     /** Observable updated when keyguardDone should be called either now or soon. */
     val keyguardDone: Flow<KeyguardDone>
 
+    /**
+     * Emits after the keyguard is done animating away.
+     *
+     * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed.
+     */
+    @Deprecated(
+        "Use KeyguardTransitionInteractor flows instead. The closest match for " +
+            "'keyguardDoneAnimationsFinished' is when the GONE transition is finished."
+    )
+    val keyguardDoneAnimationsFinished: Flow<Unit>
+
     /** Receive whether clock should be centered on lockscreen. */
     val clockShouldBeCentered: Flow<Boolean>
 
@@ -239,6 +250,17 @@
     suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone)
 
     fun setClockShouldBeCentered(shouldBeCentered: Boolean)
+
+    /**
+     * Updates signal that the keyguard done animations are finished
+     *
+     * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed.
+     */
+    @Deprecated(
+        "Use KeyguardTransitionInteractor flows instead. The closest match for " +
+            "'keyguardDoneAnimationsFinished' is when the GONE transition is finished."
+    )
+    fun keyguardDoneAnimationsFinished()
 }
 
 /** Encapsulates application state for the keyguard. */
@@ -269,6 +291,11 @@
         _keyguardDone.emit(keyguardDoneType)
     }
 
+    override val keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = MutableSharedFlow()
+    override fun keyguardDoneAnimationsFinished() {
+        keyguardDoneAnimationsFinished.tryEmit(Unit)
+    }
+
     private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
     override val animateBottomAreaDozingTransitions =
         _animateBottomAreaDozingTransitions.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index defca18..21651ba2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -318,6 +318,10 @@
         repository.ambientIndicationVisible.value = isVisible
     }
 
+    fun keyguardDoneAnimationsFinished() {
+        repository.keyguardDoneAnimationsFinished()
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
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 fb20000..e3f4739 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
@@ -31,7 +31,6 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
@@ -72,7 +71,6 @@
     private val context: Context,
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
-    @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val repository: DeviceEntryFaceAuthRepository,
     private val primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -109,7 +107,6 @@
                     fallbackToDetect = false
                 )
             }
-            .flowOn(backgroundDispatcher)
             .launchIn(applicationScope)
 
         alternateBouncerInteractor.isVisible
@@ -121,7 +118,6 @@
                     fallbackToDetect = false
                 )
             }
-            .flowOn(backgroundDispatcher)
             .launchIn(applicationScope)
 
         merge(
@@ -150,7 +146,6 @@
                     fallbackToDetect = true
                 )
             }
-            .flowOn(backgroundDispatcher)
             .launchIn(applicationScope)
 
         deviceEntryFingerprintAuthRepository.isLockedOut
@@ -163,7 +158,6 @@
                     }
                 }
             }
-            .flowOn(backgroundDispatcher)
             .launchIn(applicationScope)
 
         // User switching should stop face auth and then when it is complete we should trigger face
@@ -187,7 +181,6 @@
                     )
                 }
             }
-            .flowOn(backgroundDispatcher)
             .launchIn(applicationScope)
     }
 
@@ -302,7 +295,6 @@
                     trustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, userInfo.id)
                 }
             }
-            .flowOn(backgroundDispatcher)
             .onEach { (isAuthenticated, _) ->
                 listeners.forEach { it.onAuthenticatedChanged(isAuthenticated) }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index acfd3b0..24240df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
+import com.android.systemui.util.kotlin.logD
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -58,11 +59,14 @@
 
             observer =
                 PreviewLifecycleObserver(
-                    renderer,
                     applicationScope,
                     mainDispatcher,
+                    renderer,
                     ::destroyObserver,
                 )
+
+            logD(TAG) { "Created observer $observer" }
+
             // Destroy any previous renderer associated with this token.
             activePreviews[renderer.id]?.let { destroyObserver(it) }
             activePreviews[renderer.id] = observer
@@ -80,6 +84,8 @@
                         observer,
                     )
                 )
+            // NOTE: The process on the other side can retain messenger indefinitely.
+            // (e.g. GC might not trigger and cleanup the reference)
             val msg = Message.obtain()
             msg.replyTo = messenger
             result.putParcelable(KEY_PREVIEW_CALLBACK, msg)
@@ -99,57 +105,84 @@
         }
     }
 
-    private class PreviewLifecycleObserver(
-        private val renderer: KeyguardPreviewRenderer,
-        private val scope: CoroutineScope,
-        private val mainDispatcher: CoroutineDispatcher,
-        private val requestDestruction: (PreviewLifecycleObserver) -> Unit,
-    ) : Handler.Callback, IBinder.DeathRecipient {
+    companion object {
+        internal const val TAG = "KeyguardRemotePreviewManager"
+        @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package"
+        @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback"
+    }
+}
 
-        private var isDestroyedOrDestroying = false
+/**
+ * Handles messages from the other process and handles cleanup.
+ *
+ * NOTE: The other process might hold on to reference of this class indefinitely. It's entirely
+ * possible that GC won't trigger and we'll leak this for all times even if [onDestroy] was called.
+ * This helps make sure no non-Singleton objects are retained beyond destruction to prevent leaks.
+ */
+@VisibleForTesting(VisibleForTesting.PRIVATE)
+class PreviewLifecycleObserver(
+    private val scope: CoroutineScope,
+    private val mainDispatcher: CoroutineDispatcher,
+    renderer: KeyguardPreviewRenderer,
+    onDestroy: (PreviewLifecycleObserver) -> Unit,
+) : Handler.Callback, IBinder.DeathRecipient {
 
-        override fun handleMessage(message: Message): Boolean {
-            if (isDestroyedOrDestroying) {
-                return true
-            }
+    private var isDestroyedOrDestroying = false
+    // These two are null after destruction
+    @VisibleForTesting var renderer: KeyguardPreviewRenderer?
+    @VisibleForTesting var onDestroy: ((PreviewLifecycleObserver) -> Unit)?
 
-            when (message.what) {
-                KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
-                    message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
-                        renderer.onSlotSelected(slotId = slotId)
-                    }
-                }
-                KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> {
-                    renderer.hideSmartspace(
-                        message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
-                    )
-                }
-                else -> requestDestruction(this)
-            }
+    init {
+        this.renderer = renderer
+        this.onDestroy = onDestroy
+    }
 
+    override fun handleMessage(message: Message): Boolean {
+        if (isDestroyedOrDestroying) {
             return true
         }
 
-        override fun binderDied() {
-            requestDestruction(this)
+        when (message.what) {
+            KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
+                message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
+                    checkNotNull(renderer).onSlotSelected(slotId = slotId)
+                }
+            }
+            KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> {
+                checkNotNull(renderer)
+                    .hideSmartspace(
+                        message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
+                    )
+            }
+            else -> checkNotNull(onDestroy).invoke(this)
         }
 
-        fun onDestroy(): Pair<IBinder?, Int>? {
-            if (isDestroyedOrDestroying) {
-                return null
-            }
+        return true
+    }
 
-            isDestroyedOrDestroying = true
-            val hostToken = renderer.hostToken
+    override fun binderDied() {
+        onDestroy?.invoke(this)
+    }
+
+    fun onDestroy(): Pair<IBinder?, Int>? {
+        if (isDestroyedOrDestroying) {
+            return null
+        }
+
+        logD(TAG) { "Destroying $this" }
+
+        isDestroyedOrDestroying = true
+        return renderer?.let { rendererToDestroy ->
+            this.renderer = null
+            this.onDestroy = null
+            val hostToken = rendererToDestroy.hostToken
             hostToken?.unlinkToDeath(this, 0)
-            scope.launch(mainDispatcher) { renderer.destroy() }
-            return renderer.id
+            scope.launch(mainDispatcher) { rendererToDestroy.destroy() }
+            rendererToDestroy.id
         }
     }
 
     companion object {
         private const val TAG = "KeyguardRemotePreviewManager"
-        @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package"
-        @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 14d4b68..c87fd14 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -119,7 +119,7 @@
         ::AnimatingColorTransition
     )
 
-    val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_secondary95)
+    val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
     val surfaceColor =
         animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor ->
             val colorList = ColorStateList.valueOf(surfaceColor)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index a4088f8..0434b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles
 
+import android.app.AlertDialog
 import android.content.Intent
 import android.os.Handler
 import android.os.Looper
@@ -24,8 +25,11 @@
 import android.view.View
 import android.widget.Switch
 import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.Flags.recordIssueQsTile
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -36,7 +40,11 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.recordissue.RecordIssueDialogDelegate
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
 
 class RecordIssueTile
@@ -50,7 +58,11 @@
     metricsLogger: MetricsLogger,
     statusBarStateController: StatusBarStateController,
     activityStarter: ActivityStarter,
-    qsLogger: QSLogger
+    qsLogger: QSLogger,
+    private val keyguardDismissUtil: KeyguardDismissUtil,
+    private val keyguardStateController: KeyguardStateController,
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val sysuiDialogFactory: SystemUIDialog.Factory,
 ) :
     QSTileImpl<QSTile.BooleanState>(
         host,
@@ -76,11 +88,41 @@
             handlesLongClick = false
         }
 
-    override fun handleClick(view: View?) {
-        isRecording = !isRecording
+    @VisibleForTesting
+    public override fun handleClick(view: View?) {
+        if (isRecording) {
+            isRecording = false
+        } else {
+            mUiHandler.post { showPrompt(view) }
+        }
         refreshState()
     }
 
+    private fun showPrompt(view: View?) {
+        val dialog: AlertDialog =
+            RecordIssueDialogDelegate(sysuiDialogFactory) {
+                    isRecording = true
+                    refreshState()
+                }
+                .createDialog()
+        val dismissAction =
+            ActivityStarter.OnDismissAction {
+                // We animate from the touched view only if we are not on the keyguard, given
+                // that if we are we will dismiss it which will also collapse the shade.
+                if (view != null && !keyguardStateController.isShowing) {
+                    dialogLaunchAnimator.showFromView(
+                        dialog,
+                        view,
+                        DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC)
+                    )
+                } else {
+                    dialog.show()
+                }
+                false
+            }
+        keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true)
+    }
+
     override fun getLongClickIntent(): Intent? = null
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
new file mode 100644
index 0000000..8221c63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.recordissue
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.os.Bundle
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.PopupMenu
+import android.widget.Switch
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+class RecordIssueDialogDelegate(
+    private val factory: SystemUIDialog.Factory,
+    private val onStarted: Runnable
+) : SystemUIDialog.Delegate {
+
+    @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
+    private lateinit var issueTypeButton: Button
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        dialog.apply {
+            setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null))
+            setTitle(context.getString(R.string.qs_record_issue_label))
+            setIcon(R.drawable.qs_record_issue_icon_off)
+            setNegativeButton(R.string.cancel) { _, _ -> dismiss() }
+            setPositiveButton(R.string.qs_record_issue_start) { _, _ ->
+                onStarted.run()
+                dismiss()
+            }
+        }
+    }
+
+    override fun createDialog(): SystemUIDialog = factory.create(this)
+
+    override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        dialog.apply {
+            window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+            window?.setGravity(Gravity.CENTER)
+
+            screenRecordSwitch = requireViewById(R.id.screenrecord_switch)
+            issueTypeButton = requireViewById(R.id.issue_type_button)
+            issueTypeButton.setOnClickListener { onIssueTypeClicked(context) }
+        }
+    }
+
+    private fun onIssueTypeClicked(context: Context) {
+        val selectedCategory = issueTypeButton.text.toString()
+        val popupMenu = PopupMenu(context, issueTypeButton)
+
+        context.resources.getStringArray(R.array.qs_record_issue_types).forEachIndexed { i, cat ->
+            popupMenu.menu.add(0, 0, i, cat).apply {
+                setIcon(R.drawable.arrow_pointing_down)
+                if (selectedCategory != cat) {
+                    iconTintList = ColorStateList.valueOf(Color.TRANSPARENT)
+                }
+            }
+        }
+        popupMenu.apply {
+            setOnMenuItemClickListener {
+                issueTypeButton.text = it.title
+                true
+            }
+            setForceShowIcon(true)
+            show()
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 756c440..0c5472f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -41,6 +41,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -49,6 +50,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
@@ -210,23 +212,33 @@
     private void notifyKeyguardChanged() {
         Trace.beginSection("KeyguardStateController#notifyKeyguardChanged");
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
+        invokeForEachCallback(Callback::onKeyguardShowingChanged);
         Trace.endSection();
     }
 
     private void notifyKeyguardFaceAuthEnabledChanged() {
+        invokeForEachCallback(Callback::onFaceEnrolledChanged);
+    }
+
+    private void invokeForEachCallback(Consumer<Callback> consumer) {
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(callback -> {
+        ArrayList<Callback> copyOfCallbacks = new ArrayList<>(mCallbacks);
+        for (int i = 0; i < copyOfCallbacks.size(); i++) {
+            Callback callback = copyOfCallbacks.get(i);
+            // Temporary fix for b/315731775, callback is null even though only non-null callbacks
+            // are added to the list by addCallback
             if (callback != null) {
-                callback.onFaceEnrolledChanged();
+                consumer.accept(callback);
+            } else {
+                mLogger.log("KeyguardStateController callback is null", LogLevel.DEBUG);
             }
-        });
+        }
     }
 
     private void notifyUnlockedChanged() {
         Trace.beginSection("KeyguardStateController#notifyUnlockedChanged");
         // Copy the list to allow removal during callback.
-        new ArrayList<>(mCallbacks).forEach(Callback::onUnlockedChanged);
+        invokeForEachCallback(Callback::onUnlockedChanged);
         Trace.endSection();
     }
 
@@ -242,10 +254,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardFadingAway",
                     keyguardFadingAway ? 1 : 0);
             mKeyguardFadingAway = keyguardFadingAway;
-            ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
-            for (int i = 0; i < callbacks.size(); i++) {
-                callbacks.get(i).onKeyguardFadingAwayChanged();
-            }
+            invokeForEachCallback(Callback::onKeyguardFadingAwayChanged);
         }
     }
 
@@ -359,7 +368,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway",
                     keyguardGoingAway ? 1 : 0);
             mKeyguardGoingAway = keyguardGoingAway;
-            new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardGoingAwayChanged);
+            invokeForEachCallback(Callback::onKeyguardGoingAwayChanged);
         }
     }
 
@@ -368,7 +377,7 @@
         if (mPrimaryBouncerShowing != showing) {
             mPrimaryBouncerShowing = showing;
 
-            new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged);
+            invokeForEachCallback(Callback::onPrimaryBouncerShowingChanged);
         }
     }
 
@@ -392,13 +401,13 @@
             boolean dismissingFromTouch) {
         mDismissAmount = dismissAmount;
         mDismissingFromTouch = dismissingFromTouch;
-        new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardDismissAmountChanged);
+        invokeForEachCallback(Callback::onKeyguardDismissAmountChanged);
     }
 
     @Override
     public void setLaunchTransitionFadingAway(boolean fadingAway) {
         mLaunchTransitionFadingAway = fadingAway;
-        new ArrayList<>(mCallbacks).forEach(Callback::onLaunchTransitionFadingAwayChanged);
+        invokeForEachCallback(Callback::onLaunchTransitionFadingAwayChanged);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt
new file mode 100644
index 0000000..2f6c450
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.util.kotlin
+
+import android.util.Log
+
+/** Logs message at [Log.DEBUG] level. Won't call the lambda if [DEBUG] is not loggable. */
+inline fun logD(tag: String, messageLambda: () -> String) {
+    if (Log.isLoggable(tag, Log.DEBUG)) {
+        Log.d(tag, messageLambda.invoke())
+    }
+}
+
+/** Logs message at [Log.VERBOSE] level. Won't call the lambda if [VERBOSE] is not loggable. */
+inline fun logV(tag: String, messageLambda: () -> String) {
+    if (Log.isLoggable(tag, Log.VERBOSE)) {
+        Log.v(tag, messageLambda.invoke())
+    }
+}
+
+/** Logs message at [Log.INFO] level. Won't call the lambda if [INFO] is not loggable. */
+inline fun logI(tag: String, messageLambda: () -> String) {
+    if (Log.isLoggable(tag, Log.INFO)) {
+        Log.i(tag, messageLambda.invoke())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index ae5f625..4ef18cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -93,6 +93,7 @@
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.flags.SystemPropertiesHelper;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -190,6 +191,7 @@
     private @Mock ShadeInteractor mShadeInteractor;
     private @Mock ShadeWindowLogger mShadeWindowLogger;
     private @Mock SelectedUserInteractor mSelectedUserInteractor;
+    private @Mock KeyguardInteractor mKeyguardInteractor;
     private @Captor ArgumentCaptor<KeyguardStateController.Callback>
             mKeyguardStateControllerCallback;
     private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -1131,7 +1133,8 @@
                 () -> mDreamingToLockscreenTransitionViewModel,
                 mSystemPropertiesHelper,
                 () -> mock(WindowManagerLockscreenVisibilityManager.class),
-                mSelectedUserInteractor);
+                mSelectedUserInteractor,
+                mKeyguardInteractor);
         mViewMediator.start();
 
         mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
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 4ab8e28..6eb95bd 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
@@ -137,7 +137,6 @@
                 mContext,
                 testScope.backgroundScope,
                 dispatcher,
-                dispatcher,
                 faceAuthRepository,
                 {
                     PrimaryBouncerInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index d8199c5..e9714dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -30,12 +31,19 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isA
 import org.mockito.Mock
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 /**
@@ -53,15 +61,22 @@
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var qsLogger: QSLogger
+    @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var dialogLauncherAnimator: DialogLaunchAnimator
+    @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+    @Mock private lateinit var dialog: SystemUIDialog
 
+    private lateinit var testableLooper: TestableLooper
     private lateinit var tile: RecordIssueTile
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(host.context).thenReturn(mContext)
+        whenever(dialogFactory.create(any())).thenReturn(dialog)
 
-        val testableLooper = TestableLooper.get(this)
+        testableLooper = TestableLooper.get(this)
         tile =
             RecordIssueTile(
                 host,
@@ -72,7 +87,11 @@
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
-                qsLogger
+                qsLogger,
+                keyguardDismissUtil,
+                keyguardStateController,
+                dialogLauncherAnimator,
+                dialogFactory
             )
     }
 
@@ -119,4 +138,18 @@
 
         assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE)
     }
+
+    @Test
+    fun showPrompt_shouldUseKeyguardDismissUtil_ToShowDialog() {
+        tile.isRecording = false
+        tile.handleClick(null)
+        testableLooper.processAllMessages()
+
+        verify(keyguardDismissUtil)
+            .executeWhenUnlocked(
+                isA(ActivityStarter.OnDismissAction::class.java),
+                eq(false),
+                eq(true)
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
new file mode 100644
index 0000000..bbc59d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.recordissue
+
+import android.app.Dialog
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.widget.Button
+import android.widget.Switch
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.model.SysUiState
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class RecordIssueDialogDelegateTest : SysuiTestCase() {
+
+    private lateinit var dialog: SystemUIDialog
+    private lateinit var latch: CountDownLatch
+
+    @Before
+    fun setup() {
+        val dialogFactory =
+            SystemUIDialog.Factory(
+                context,
+                mock<FeatureFlags>(),
+                mock<SystemUIDialogManager>(),
+                mock<SysUiState>().apply {
+                    whenever(setFlag(anyInt(), anyBoolean())).thenReturn(this)
+                },
+                mock<BroadcastDispatcher>(),
+                mock<DialogLaunchAnimator>()
+            )
+
+        latch = CountDownLatch(1)
+        dialog = RecordIssueDialogDelegate(dialogFactory) { latch.countDown() }.createDialog()
+        dialog.show()
+    }
+
+    @After
+    fun teardown() {
+        dialog.dismiss()
+    }
+
+    @Test
+    fun dialog_hasCorrectUiElements_afterCreation() {
+        dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+        dialog.requireViewById<Button>(R.id.issue_type_button)
+
+        assertThat(dialog.getButton(Dialog.BUTTON_POSITIVE).text)
+            .isEqualTo(context.getString(R.string.qs_record_issue_start))
+        assertThat(dialog.getButton(Dialog.BUTTON_NEGATIVE).text)
+            .isEqualTo(context.getString(R.string.cancel))
+    }
+
+    @Test
+    fun onStarted_isCalled_afterStartButtonIsClicked() {
+        dialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick()
+        latch.await(1L, TimeUnit.MILLISECONDS)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index 0f33aaf..2c7b606 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -56,6 +56,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -138,6 +139,7 @@
     }
 
     @Test
+    @Ignore("b/315848285")
     public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() {
         ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
             assertThat(resultCode).isEqualTo(RESULT_OK);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index c5d745a..4200f05 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -41,6 +41,10 @@
     private val _deferKeyguardDone: MutableSharedFlow<KeyguardDone> = MutableSharedFlow()
     override val keyguardDone: Flow<KeyguardDone> = _deferKeyguardDone
 
+    private val _keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> =
+        MutableSharedFlow(extraBufferCapacity = 1)
+    override val keyguardDoneAnimationsFinished: Flow<Unit> = _keyguardDoneAnimationsFinished
+
     private val _clockShouldBeCentered = MutableStateFlow<Boolean>(true)
     override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered
 
@@ -176,6 +180,10 @@
         _deferKeyguardDone.emit(timing)
     }
 
+    override fun keyguardDoneAnimationsFinished() {
+        _keyguardDoneAnimationsFinished.tryEmit(Unit)
+    }
+
     override fun setClockShouldBeCentered(shouldBeCentered: Boolean) {
         _clockShouldBeCentered.value = shouldBeCentered
     }
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ab678d9..ca6fefd 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -27,3 +27,10 @@
   description: "Mitigation for view state reset to empty causing no save dialog to show issue"
   bug: "297976948"
 }
+
+flag {
+  name: "ignore_invisible_view_group_in_assist_structure"
+  namespace: "autofill"
+  description: "Mitigation for autofill providers miscalculating view visibility"
+  bug: "291795358"
+}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 09e7986..136692e 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1426,14 +1426,19 @@
 
     /**
      * Checks if package is quarantined for a specific user.
+     *
+     * @throws PackageManager.NameNotFoundException if the package is not found
      */
-    public abstract boolean isPackageQuarantined(@NonNull String packageName,
-            @UserIdInt int userId);
+    public abstract boolean isPackageQuarantined(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException;
 
     /**
      * Checks if package is stopped for a specific user.
+     *
+     * @throws PackageManager.NameNotFoundException if the package is not found
      */
-    public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId);
+    public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException;
 
     /**
      * Sends the PACKAGE_RESTARTED broadcast.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7191684..df8f17a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5175,6 +5175,8 @@
             return null;
         }
 
+        final long startTimeNs = SystemClock.elapsedRealtimeNanos();
+
         if (DEBUG_SERVICE) {
             Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired);
         }
@@ -5333,9 +5335,14 @@
                 bringDownServiceLocked(r, enqueueOomAdj);
                 return msg;
             }
+            mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r,
+                    hostingRecord, true);
             if (isolated) {
                 r.isolationHostProc = app;
             }
+        } else {
+            mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r,
+                    hostingRecord, false);
         }
 
         if (r.fgRequired) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d9dcfab..2ee39c57 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1103,9 +1103,51 @@
 
     private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
             new ActivityMetricsLaunchObserver() {
+
         @Override
-        public void onActivityLaunched(long id, ComponentName name, int temperature) {
+        public void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
+            synchronized (this) {
+                mProcessList.getAppStartInfoTracker().onIntentStarted(intent, timestampNanos);
+            }
+        }
+
+        @Override
+        public void onIntentFailed(long id) {
+            mProcessList.getAppStartInfoTracker().onIntentFailed(id);
+        }
+
+        @Override
+        public void onActivityLaunched(long id, ComponentName name, int temperature, int userId) {
             mAppProfiler.onActivityLaunched();
+            synchronized (ActivityManagerService.this) {
+                ProcessRecord record = null;
+                try {
+                    record = getProcessRecordLocked(name.getPackageName(), mContext
+                            .getPackageManager().getPackageUidAsUser(name.getPackageName(), 0,
+                            userId));
+                } catch (NameNotFoundException nnfe) {
+                    // Ignore, record will be lost.
+                }
+                mProcessList.getAppStartInfoTracker().onActivityLaunched(id, name, temperature,
+                        record);
+            }
+        }
+
+        @Override
+        public void onActivityLaunchCancelled(long id) {
+            mProcessList.getAppStartInfoTracker().onActivityLaunchCancelled(id);
+        }
+
+        @Override
+        public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos,
+                int launchMode) {
+            mProcessList.getAppStartInfoTracker().onActivityLaunchFinished(id, name,
+                    timestampNanos, launchMode);
+        }
+
+        @Override
+        public void onReportFullyDrawn(long id, long timestampNanos) {
+            mProcessList.getAppStartInfoTracker().onReportFullyDrawn(id, timestampNanos);
         }
     };
 
@@ -4488,13 +4530,13 @@
     @GuardedBy("this")
     private void attachApplicationLocked(@NonNull IApplicationThread thread,
             int pid, int callingUid, long startSeq) {
-
         // Find the application record that is being attached...  either via
         // the pid if we are running in multiple processes, or just pull the
         // next app record if we are emulating process with anonymous threads.
         ProcessRecord app;
         long startTime = SystemClock.uptimeMillis();
         long bindApplicationTimeMillis;
+        long bindApplicationTimeNanos;
         if (pid != MY_PID && pid >= 0) {
             synchronized (mPidsSelfLocked) {
                 app = mPidsSelfLocked.get(pid);
@@ -4698,6 +4740,7 @@
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
             bindApplicationTimeMillis = SystemClock.uptimeMillis();
+            bindApplicationTimeNanos = SystemClock.elapsedRealtimeNanos();
             mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             if (mPlatformCompat != null) {
@@ -4754,6 +4797,8 @@
             }
 
             app.setBindApplicationTime(bindApplicationTimeMillis);
+            mProcessList.getAppStartInfoTracker()
+                    .reportBindApplicationTimeNanos(app, bindApplicationTimeNanos);
 
             // Make app active after binding application or client may be running requests (e.g
             // starting activities) before it is ready.
@@ -9799,12 +9844,12 @@
             final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid,
                         "getHistoricalProcessStartReasons");
             if (uid != INVALID_UID) {
-                mProcessList.mAppStartInfoTracker.getStartInfo(
+                mProcessList.getAppStartInfoTracker().getStartInfo(
                         packageName, userId, callingPid, maxNum, results);
             }
         } else {
             // If no package name is given, use the caller's uid as the filter uid.
-            mProcessList.mAppStartInfoTracker.getStartInfo(
+            mProcessList.getAppStartInfoTracker().getStartInfo(
                     packageName, callingUid, callingPid, maxNum, results);
         }
         return new ParceledListSlice<ApplicationStartInfo>(results);
@@ -9822,7 +9867,7 @@
         }
 
         final int callingUid = Binder.getCallingUid();
-        mProcessList.mAppStartInfoTracker.addStartInfoCompleteListener(listener, callingUid);
+        mProcessList.getAppStartInfoTracker().addStartInfoCompleteListener(listener, callingUid);
     }
 
 
@@ -9836,7 +9881,7 @@
         }
 
         final int callingUid = Binder.getCallingUid();
-        mProcessList.mAppStartInfoTracker.clearStartInfoCompleteListener(callingUid, true);
+        mProcessList.getAppStartInfoTracker().clearStartInfoCompleteListener(callingUid, true);
     }
 
     @Override
@@ -10138,7 +10183,7 @@
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
-                mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage);
+                mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage);
                 pw.println("-------------------------------------------------------------------------------");
                 mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage);
             }
@@ -10541,7 +10586,7 @@
                     dumpPackage = args[opti];
                     opti++;
                 }
-                mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage);
+                mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage);
             } else if ("exit-info".equals(cmd)) {
                 if (opti < args.length) {
                     dumpPackage = args[opti];
@@ -13831,6 +13876,7 @@
     // activity manager to announce its creation.
     public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId,
             @BackupDestination int backupDestination) {
+        long startTimeNs = SystemClock.elapsedRealtimeNanos();
         if (DEBUG_BACKUP) {
             Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode
                     + " targetUserId=" + targetUserId + " callingUid = " + Binder.getCallingUid()
@@ -13906,15 +13952,20 @@
                             ? new ComponentName(app.packageName, app.backupAgentName)
                             : new ComponentName("android", "FullBackupAgent");
 
-            // startProcessLocked() returns existing proc's record if it's already running
-            ProcessRecord proc = startProcessLocked(app.processName, app,
-                    false, 0,
-                    new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName),
-                    ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false);
+            ProcessRecord proc = getProcessRecordLocked(app.processName, app.uid);
+            boolean isProcessStarted = proc != null;
+            if (!isProcessStarted) {
+                proc = startProcessLocked(app.processName, app,
+                  false, 0,
+                  new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName),
+                  ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false);
+            }
             if (proc == null) {
                 Slog.e(TAG, "Unable to start backup agent process " + r);
                 return false;
             }
+            mProcessList.getAppStartInfoTracker().handleProcessBackupStart(startTimeNs, proc, r,
+                    !isProcessStarted);
 
             // If the app is a regular app (uid >= 10000) and not the system server or phone
             // process, etc, then mark it as being in full backup so that certain calls to the
@@ -18741,8 +18792,12 @@
                     // If the process is known as top app, set a hint so when the process is
                     // started, the top priority can be applied immediately to avoid cpu being
                     // preempted by other processes before attaching the process of top app.
-                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
-                            new HostingRecord(hostingType, hostingName, isTop),
+                    final long startTimeNs = SystemClock.elapsedRealtimeNanos();
+                    HostingRecord hostingRecord =
+                            new HostingRecord(hostingType, hostingName, isTop);
+                    ProcessRecord rec = getProcessRecordLocked(processName, info.uid);
+                    ProcessRecord app = startProcessLocked(processName, info, knownToBeDead,
+                            0 /* intentFlags */, hostingRecord,
                             ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                             false /* isolated */);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f3b2ef3..ae0cd65 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1362,7 +1362,7 @@
             }
             userId = user.id;
         }
-        mInternal.mProcessList.mAppStartInfoTracker
+        mInternal.mProcessList.getAppStartInfoTracker()
                 .clearHistoryProcessStartInfo(packageName, userId);
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index edca74f..82e554e 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -22,11 +22,12 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
-import android.app.ActivityOptions;
+import android.annotation.NonNull;
 import android.app.ApplicationStartInfo;
 import android.app.Flags;
 import android.app.IApplicationStartInfoCompleteListener;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -138,6 +139,15 @@
     /** The path to the historical proc start info file, persisted in the storage. */
     @VisibleForTesting File mProcStartInfoFile;
 
+
+    /**
+     * Temporary list of records that have not been completed.
+     *
+     * Key is timestamp of launch from {@link #ActivityMetricsLaunchObserver}.
+     */
+    @GuardedBy("mLock")
+    private ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();
+
     AppStartInfoTracker() {
         mCallbacks = new SparseArray<>();
         mData = new ProcessMap<AppStartInfoContainer>();
@@ -174,68 +184,99 @@
         });
     }
 
-    void handleProcessColdStarted(long startTimeNs, HostingRecord hostingRecord,
-            ProcessRecord app) {
-        synchronized (mLock) {
-            if (!mEnabled) {
-                return;
-            }
-            ApplicationStartInfo start = new ApplicationStartInfo();
-            addBaseFieldsFromProcessRecord(start, app);
-            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
-            start.addStartupTimestamp(
-                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
-            start.addStartupTimestamp(
-                    ApplicationStartInfo.START_TIMESTAMP_FORK, app.getStartElapsedTime());
-            start.setStartType(ApplicationStartInfo.START_TYPE_COLD);
-            start.setReason(ApplicationStartInfo.START_REASON_OTHER);
-            addStartInfoLocked(start);
-        }
-    }
-
-    public void handleProcessActivityWarmOrHotStarted(long startTimeNs,
-            ActivityOptions activityOptions, Intent intent) {
+    void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
         synchronized (mLock) {
             if (!mEnabled) {
                 return;
             }
             ApplicationStartInfo start = new ApplicationStartInfo();
             start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
-            start.addStartupTimestamp(
-                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
             start.setIntent(intent);
-            start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER);
-            if (activityOptions != null) {
-                start.setProcessName(activityOptions.getPackageName());
-            }
-            start.setStartType(ApplicationStartInfo.START_TYPE_WARM);
+            start.setStartType(ApplicationStartInfo.START_TYPE_UNSET);
+            start.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_LAUNCH, timestampNanos);
             if (intent != null && intent.getCategories() != null
                     && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
                 start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER);
             } else {
                 start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY);
             }
-            addStartInfoLocked(start);
+            mInProgRecords.put(timestampNanos, start);
         }
     }
 
-    public void handleProcessActivityStartedFromRecents(long startTimeNs,
-            ActivityOptions activityOptions) {
+    void onIntentFailed(long id) {
         synchronized (mLock) {
             if (!mEnabled) {
                 return;
             }
-            ApplicationStartInfo start = new ApplicationStartInfo();
-            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
-            start.addStartupTimestamp(
-                    ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
-            if (activityOptions != null) {
-                start.setIntent(activityOptions.getResultData());
-                start.setProcessName(activityOptions.getPackageName());
+            if (!mInProgRecords.containsKey(id)) {
+                return;
             }
-            start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER_RECENTS);
-            start.setStartType(ApplicationStartInfo.START_TYPE_WARM);
-            addStartInfoLocked(start);
+            mInProgRecords.get(id).setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
+            mInProgRecords.remove(id);
+        }
+    }
+
+    void onActivityLaunched(long id, ComponentName name, long temperature, ProcessRecord app) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (!mInProgRecords.containsKey(id)) {
+                return;
+            }
+            if (app != null) {
+                ApplicationStartInfo info = mInProgRecords.get(id);
+                info.setStartType((int) temperature);
+                addBaseFieldsFromProcessRecord(info, app);
+                addStartInfoLocked(info);
+            } else {
+                mInProgRecords.remove(id);
+            }
+        }
+    }
+
+    void onActivityLaunchCancelled(long id) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (!mInProgRecords.containsKey(id)) {
+                return;
+            }
+            ApplicationStartInfo info = mInProgRecords.get(id);
+            info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
+            mInProgRecords.remove(id);
+        }
+    }
+
+    void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos,
+            int launchMode) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (!mInProgRecords.containsKey(id)) {
+                return;
+            }
+            ApplicationStartInfo info = mInProgRecords.get(id);
+            info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN);
+            info.setLaunchMode(launchMode);
+        }
+    }
+
+    void onReportFullyDrawn(long id, long timestampNanos) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                return;
+            }
+            if (!mInProgRecords.containsKey(id)) {
+                return;
+            }
+            ApplicationStartInfo info = mInProgRecords.get(id);
+            info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
+                    timestampNanos);
+            mInProgRecords.remove(id);
         }
     }
 
@@ -347,7 +388,8 @@
                 ApplicationStartInfo.START_TIMESTAMP_APPLICATION_ONCREATE);
     }
 
-    void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) {
+    /** Report a bind application timestamp to add to {@link ApplicationStartInfo}. */
+    public void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) {
         addTimestampToStart(app, timeNs,
                 ApplicationStartInfo.START_TIMESTAMP_BIND_APPLICATION);
     }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4ff34b1..3156e9d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -498,7 +498,7 @@
 
     /** Manages the {@link android.app.ApplicationStartInfo} records. */
     @GuardedBy("mAppStartInfoTracker")
-    final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker();
+    private final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker();
 
     /**
      * The currently running SDK sandbox processes for a uid.
@@ -1523,6 +1523,10 @@
         return mCachedRestoreLevel;
     }
 
+    AppStartInfoTracker getAppStartInfoTracker() {
+        return mAppStartInfoTracker;
+    }
+
     /**
      * Set the out-of-memory badness adjustment for a process.
      * If {@code pid <= 0}, this method will be a no-op.
@@ -2572,6 +2576,7 @@
             boolean isSdkSandbox, int sdkSandboxUid, String sdkSandboxClientAppPackage,
             String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
         long startTime = SystemClock.uptimeMillis();
+        final long startTimeNs = SystemClock.elapsedRealtimeNanos();
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(processName, info.uid);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 1e5e147..df179a9 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1294,8 +1294,8 @@
         if (android.content.pm.Flags.stayStopped()) {
             try {
                 return mPackageManagerInternal.isPackageStopped(packageName, userId);
-            } catch (IllegalArgumentException e) {
-                Log.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName);
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "Couldn't determine stopped state for unknown package: " + packageName);
             }
         }
         return false;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 1687157..5831b29 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -194,10 +194,14 @@
             addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                 @Override
                 public void onComplete(int result) {
-                    if (result != HdmiControlManager.RESULT_SUCCESS) {
+                    if (!mService.getLocalActiveSource().isValid()
+                            && result != HdmiControlManager.RESULT_SUCCESS) {
                         mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
                                 getDeviceInfo().getLogicalAddress(),
                                 getDeviceInfo().getPhysicalAddress()));
+                        updateActiveSource(getDeviceInfo().getLogicalAddress(),
+                                getDeviceInfo().getPhysicalAddress(),
+                                "RequestActiveSourceAction#finishWithCallback()");
                     }
                 }
             }));
@@ -257,6 +261,7 @@
         if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) {
             return;
         }
+        removeAction(RequestActiveSourceAction.class);
         if (targetAddress == Constants.ADDR_INTERNAL) {
             handleSelectInternalSource();
             // Switching to internal source is always successful even when CEC control is disabled.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index f526dbe..4089a81 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -215,10 +215,20 @@
     /**
      * Switch the keyboard layout in response to a keyboard shortcut.
      *
-     * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the
-     *                  previous subtype
+     * @param direction         {@code 1} to switch to the next subtype, {@code -1} to switch to the
+     *                          previous subtype
+     * @param displayId         the display to which the keyboard layout switch shortcut is
+     *                          dispatched. Note that there is no guarantee that an IME is
+     *                          associated with this display. This is more or less than a hint for
+     *                          cases when no IME is running for the given targetWindowToken. There
+     *                          is a longstanding discussion whether we should allow users to
+     *                          rotate keyboard layout even when there is no edit field, and this
+     *                          displayID would be helpful for such a situation.
+     * @param targetWindowToken the window token to which other keys are being sent while handling
+     *                          this shortcut.
      */
-    public abstract void switchKeyboardLayout(int direction);
+    public abstract void onSwitchKeyboardLayoutShortcut(int direction, int displayId,
+            IBinder targetWindowToken);
 
     /**
      * Returns true if any InputConnection is currently active.
@@ -314,7 +324,8 @@
                 }
 
                 @Override
-                public void switchKeyboardLayout(int direction) {
+                public void onSwitchKeyboardLayoutShortcut(int direction, int displayId,
+                        IBinder targetWindowToken) {
                 }
 
                 @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 30e9f5b..c440a64 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5763,7 +5763,8 @@
         }
 
         @Override
-        public void switchKeyboardLayout(int direction) {
+        public void onSwitchKeyboardLayoutShortcut(int direction, int displayId,
+                IBinder targetWindowToken) {
             synchronized (ImfLock.class) {
                 switchKeyboardLayoutLocked(direction);
             }
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index bab3cbe..9c27c22 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.contexthub.HostEndpointInfo;
+import android.hardware.contexthub.MessageDeliveryStatus;
 import android.hardware.contexthub.NanSessionRequest;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -467,6 +468,11 @@
                 // TODO(271471342): Implement
             }
 
+            public void handleMessageDeliveryStatus(char hostEndPointId,
+                    MessageDeliveryStatus messageDeliveryStatus) {
+                // TODO(b/312417087): Implement reliable message support
+            }
+
             public byte[] getUuid() {
                 return UUID;
             }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 42c2548..0c2eee5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -209,7 +209,7 @@
  *   <li>Protect each user's data using their SP.  For example, use the SP to encrypt/decrypt the
  *   user's credential-encrypted (CE) key for file-based encryption (FBE).</li>
  *
- *   <li>Generate, protect, and use profile passwords for managed profiles.</li>
+ *   <li>Generate, protect, and use unified profile passwords.</li>
  *
  *   <li>Support unlocking the SP by alternative means: resume-on-reboot (reboot escrow) for easier
  *   OTA updates, and escrow tokens when set up by the Device Policy Controller (DPC).</li>
@@ -287,7 +287,7 @@
 
     private final java.security.KeyStore mJavaKeyStore;
     private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
-    private ManagedProfilePasswordCache mManagedProfilePasswordCache;
+    private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache;
 
     private final RebootEscrowManager mRebootEscrowManager;
 
@@ -404,7 +404,8 @@
         for (int i = 0; i < newPasswordChars.length; i++) {
             newPassword[i] = (byte) newPasswordChars[i];
         }
-        LockscreenCredential credential = LockscreenCredential.createManagedPassword(newPassword);
+        LockscreenCredential credential =
+                LockscreenCredential.createUnifiedProfilePassword(newPassword);
         Arrays.fill(newPasswordChars, '\u0000');
         Arrays.fill(newPassword, (byte) 0);
         Arrays.fill(randomLockSeed, (byte) 0);
@@ -424,7 +425,7 @@
         if (!isCredentialSharableWithParent(profileUserId)) {
             return;
         }
-        // Do not tie profile when work challenge is enabled
+        // Do not tie profile when separate challenge is enabled
         if (getSeparateProfileChallengeEnabledInternal(profileUserId)) {
             return;
         }
@@ -462,7 +463,7 @@
             setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId,
                     /* isLockTiedToParent= */ true);
             tieProfileLockToParent(profileUserId, parent.id, unifiedProfilePassword);
-            mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword,
+            mUnifiedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword,
                     parentSid);
         }
     }
@@ -620,9 +621,9 @@
             }
         }
 
-        public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache(
+        public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
                 java.security.KeyStore ks) {
-            return new ManagedProfilePasswordCache(ks);
+            return new UnifiedProfilePasswordCache(ks);
         }
 
         public boolean isHeadlessSystemUserMode() {
@@ -665,7 +666,7 @@
         mGatekeeperPasswords = new LongSparseArray<>();
 
         mSpManager = injector.getSyntheticPasswordManager(mStorage);
-        mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);
+        mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore);
         mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
 
         mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
@@ -689,8 +690,8 @@
     }
 
     /**
-     * If the account is credential-encrypted, show notification requesting the user to unlock the
-     * device.
+     * If the user is a managed profile whose credential-encrypted storage is locked, show a
+     * notification requesting the user to unlock the device.
      */
     private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId, String reason) {
         final UserInfo user = mUserManager.getUserInfo(userId);
@@ -846,7 +847,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                // Hide notification first, as tie managed profile lock takes time
+                // Hide notification first, as tie profile lock takes time
                 hideEncryptionNotification(new UserHandle(userId));
 
                 if (isCredentialSharableWithParent(userId)) {
@@ -1458,13 +1459,13 @@
 
         cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
         decryptionResult = cipher.doFinal(encryptedPassword);
-        LockscreenCredential credential = LockscreenCredential.createManagedPassword(
+        LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword(
                 decryptionResult);
         Arrays.fill(decryptionResult, (byte) 0);
         try {
             long parentSid = getGateKeeperService().getSecureUserId(
                     mUserManager.getProfileParent(userId).id);
-            mManagedProfilePasswordCache.storePassword(userId, credential, parentSid);
+            mUnifiedProfilePasswordCache.storePassword(userId, credential, parentSid);
         } catch (RemoteException e) {
             Slogf.w(TAG, "Failed to talk to GateKeeper service", e);
         }
@@ -1550,7 +1551,7 @@
                         // so it goes into the cache
                         getDecryptedPasswordForTiedProfile(profile.id);
                     } catch (GeneralSecurityException | IOException e) {
-                        Slog.d(TAG, "Cache work profile password failed", e);
+                        Slog.d(TAG, "Cache unified profile password failed", e);
                     }
                 }
             }
@@ -1604,19 +1605,19 @@
     }
 
     /**
-     * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them
+     * Synchronize all profile's challenge of the given user if it's unified: tie or clear them
      * depending on the parent user's secure state.
      *
-     * When clearing tied work challenges, a pre-computed password table for profiles are required,
-     * since changing password for profiles requires existing password, and existing passwords can
-     * only be computed before the parent user's password is cleared.
+     * When clearing tied challenges, a pre-computed password table for profiles are required, since
+     * changing password for profiles requires existing password, and existing passwords can only be
+     * computed before the parent user's password is cleared.
      *
      * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this
      * method again on profiles. However the recursion is guaranteed to terminate as this method
      * terminates when the user is a profile that shares lock credentials with parent.
      * (e.g. managed and clone profile).
      */
-    private void synchronizeUnifiedWorkChallengeForProfiles(int userId,
+    private void synchronizeUnifiedChallengeForProfiles(int userId,
             Map<Integer, LockscreenCredential> profilePasswordMap) {
         if (isCredentialSharableWithParent(userId)) {
             return;
@@ -1635,7 +1636,7 @@
                     tieProfileLockIfNecessary(profileUserId,
                             LockscreenCredential.createNone());
                 } else {
-                    // We use cached work profile password computed before clearing the parent's
+                    // We use cached profile password computed before clearing the parent's
                     // credential, otherwise they get lost
                     if (profilePasswordMap != null
                             && profilePasswordMap.containsKey(profileUserId)) {
@@ -1777,7 +1778,7 @@
                 notifyPasswordChanged(credential, userId);
             }
             if (isCredentialSharableWithParent(userId)) {
-                // Make sure the profile doesn't get locked straight after setting work challenge.
+                // Make sure the profile doesn't get locked straight after setting challenge.
                 setDeviceUnlockedForUser(userId);
             }
             notifySeparateProfileChallengeChanged(userId);
@@ -2368,7 +2369,7 @@
         }
 
         try {
-            // Unlock work profile, and work profile with unified lock must use password only
+            // Unlock profile with unified lock
             return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
                     userId, null /* progressCallback */, flags);
         } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
@@ -2492,7 +2493,7 @@
         mStrongAuth.removeUser(userId);
 
         AndroidKeyStoreMaintenance.onUserRemoved(userId);
-        mManagedProfilePasswordCache.removePassword(userId);
+        mUnifiedProfilePasswordCache.removePassword(userId);
 
         gateKeeperClearSecureUserId(userId);
         removeKeystoreProfileKey(userId);
@@ -2982,7 +2983,7 @@
                 credential, sp, userId);
         final Map<Integer, LockscreenCredential> profilePasswords;
         if (!credential.isNone()) {
-            // not needed by synchronizeUnifiedWorkChallengeForProfiles()
+            // not needed by synchronizeUnifiedChallengeForProfiles()
             profilePasswords = null;
 
             if (!mSpManager.hasSidForUser(userId)) {
@@ -2993,8 +2994,8 @@
                 }
             }
         } else {
-            // Cache all profile password if they use unified work challenge. This will later be
-            // used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles()
+            // Cache all profile password if they use unified challenge. This will later be used to
+            // clear the profile's password in synchronizeUnifiedChallengeForProfiles().
             profilePasswords = getDecryptedPasswordsForAllTiedProfiles(userId);
 
             mSpManager.clearSidForUser(userId);
@@ -3010,10 +3011,10 @@
         }
         setCurrentLskfBasedProtectorId(newProtectorId, userId);
         LockPatternUtils.invalidateCredentialTypeCache();
-        synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
+        synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
 
         setUserPasswordMetrics(credential, userId);
-        mManagedProfilePasswordCache.removePassword(userId);
+        mUnifiedProfilePasswordCache.removePassword(userId);
         if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
             mSpManager.destroyAllWeakTokenBasedProtectors(userId);
         }
@@ -3114,7 +3115,7 @@
                 try {
                     currentCredential = getDecryptedPasswordForTiedProfile(userId);
                 } catch (Exception e) {
-                    Slog.e(TAG, "Failed to get work profile credential", e);
+                    Slog.e(TAG, "Failed to get unified profile password", e);
                     return null;
                 }
             }
@@ -3284,7 +3285,7 @@
     @Override
     public boolean tryUnlockWithCachedUnifiedChallenge(int userId) {
         checkPasswordReadPermission();
-        try (LockscreenCredential cred = mManagedProfilePasswordCache.retrievePassword(userId)) {
+        try (LockscreenCredential cred = mUnifiedProfilePasswordCache.retrievePassword(userId)) {
             if (cred == null) {
                 return false;
             }
@@ -3296,7 +3297,7 @@
     @Override
     public void removeCachedUnifiedChallenge(int userId) {
         checkWritePermission();
-        mManagedProfilePasswordCache.removePassword(userId);
+        mUnifiedProfilePasswordCache.removePassword(userId);
     }
 
     static String timestampToString(long timestamp) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 1e8b387..6d123cc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -501,10 +501,10 @@
         final UserInfo parentInfo = um.getProfileParent(userId);
 
         if (parentInfo == null) {
-            // This user owns its lock settings files - safe to delete them
+            // Delete files specific to non-profile users.
             deleteFile(getRebootEscrowFile(userId));
         } else {
-            // Managed profile
+            // Delete files specific to profile users.
             removeChildProfileLock(userId);
         }
 
diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java
similarity index 84%
rename from services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
rename to services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java
index 1298fe8f..21caf76 100644
--- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
+++ b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java
@@ -43,30 +43,31 @@
 import javax.crypto.spec.GCMParameterSpec;
 
 /**
- * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using
- * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified
- * work challenge is normally secured.
- *
- * <p> The cache is filled whenever the managed profile's unified challenge is created or derived
- * (as part of the parent user's credential verification flow). It's removed when the profile is
- * deleted or a (separate) lockscreen credential is explicitly set on the profile. There is also
- * an ADB command to evict the cache "cmd lock_settings remove-cache --user X", to assist
- * development and testing.
-
- * <p> The encrypted credential is stored in-memory only so the cache does not persist across
- * reboots.
+ * An in-memory cache for unified profile passwords.  A "unified profile password" is the random
+ * password that the system automatically generates and manages for each profile that uses a unified
+ * challenge and where the parent user has a secure lock screen.
+ * <p>
+ * Each password in this cache is encrypted by a Keystore key that is auth-bound to the parent user.
+ * This is very similar to how the password is protected on-disk, but the in-memory cache uses a
+ * much longer timeout on the keys: 7 days instead of 30 seconds.  This enables use cases like
+ * unpausing work apps without requiring authentication as frequently.
+ * <p>
+ * Unified profile passwords are cached when they are created, or when they are decrypted as part of
+ * the parent user's LSKF verification flow.  They are removed when the profile is deleted or when a
+ * separate challenge is explicitly set on the profile.  There is also an ADB command to evict a
+ * cached password, "locksettings remove-cache --user X", to assist development and testing.
  */
 @VisibleForTesting // public visibility is needed for Mockito
-public class ManagedProfilePasswordCache {
+public class UnifiedProfilePasswordCache {
 
-    private static final String TAG = "ManagedProfilePasswordCache";
+    private static final String TAG = "UnifiedProfilePasswordCache";
     private static final int KEY_LENGTH = 256;
     private static final int CACHE_TIMEOUT_SECONDS = (int) TimeUnit.DAYS.toSeconds(7);
 
     private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>();
     private final KeyStore mKeyStore;
 
-    public ManagedProfilePasswordCache(KeyStore keyStore) {
+    public UnifiedProfilePasswordCache(KeyStore keyStore) {
         mKeyStore = keyStore;
     }
 
@@ -151,7 +152,8 @@
                 Slog.d(TAG, "Cannot decrypt", e);
                 return null;
             }
-            LockscreenCredential result = LockscreenCredential.createManagedPassword(credential);
+            LockscreenCredential result =
+                    LockscreenCredential.createUnifiedProfilePassword(credential);
             Arrays.fill(credential, (byte) 0);
             return result;
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a62d8b8..66a9740 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5406,6 +5406,7 @@
         private void validateAutomaticZenRule(AutomaticZenRule rule) {
             Objects.requireNonNull(rule, "automaticZenRule is null");
             Objects.requireNonNull(rule.getName(), "Name is null");
+            rule.validate();
             if (rule.getOwner() == null
                     && rule.getConfigurationActivity() == null) {
                 throw new NullPointerException(
@@ -5463,6 +5464,7 @@
         public void setAutomaticZenRuleState(String id, Condition condition) {
             Objects.requireNonNull(id, "id is null");
             Objects.requireNonNull(condition, "Condition is null");
+            condition.validate();
 
             enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7ec94c3..3f8b595 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -627,6 +627,7 @@
             try {
                 ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
                 rule.name = applicationInfo.loadLabel(mPm).toString();
+                rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon);
             } catch (PackageManager.NameNotFoundException e) {
                 // Should not happen, since it's the app calling us (?)
                 Log.w(TAG, "Package not found for creating implicit zen rule");
@@ -634,6 +635,9 @@
             }
         });
 
+        rule.type = AutomaticZenRule.TYPE_OTHER;
+        rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description,
+                rule.name);
         rule.condition = null;
         rule.conditionId = new Uri.Builder()
                 .scheme(Condition.SCHEME)
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index bd725ed..27f4e11 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -508,12 +508,15 @@
 
     boolean getApplicationHiddenSettingAsUser(@NonNull String packageName, @UserIdInt int userId);
 
-    boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId);
+    boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException;
 
-    boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId);
+    boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException;
 
     /** Check if the package is in a stopped state for a given user. */
-    boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId);
+    boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException;
 
     boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId);
 
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2dc3fb7..abfd571 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4980,31 +4980,34 @@
         }
     }
 
-    private PackageUserStateInternal getUserStageOrDefaultForUser(@NonNull String packageName,
-            int userId) {
+    private PackageUserStateInternal getUserStateOrDefaultForUser(@NonNull String packageName,
+            int userId) throws PackageManager.NameNotFoundException {
         final int callingUid = Binder.getCallingUid();
         enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                 false /* checkShell */, "when asking about packages for user " + userId);
         final PackageStateInternal ps = mSettings.getPackage(packageName);
         if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
-            throw new IllegalArgumentException("Unknown target package: " + packageName);
+            throw new PackageManager.NameNotFoundException(packageName);
         }
         return ps.getUserStateOrDefault(userId);
     }
 
     @Override
-    public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) {
-        return getUserStageOrDefaultForUser(packageName, userId).isSuspended();
+    public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId)
+            throws PackageManager.NameNotFoundException {
+        return getUserStateOrDefaultForUser(packageName, userId).isSuspended();
     }
 
     @Override
-    public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) {
-        return getUserStageOrDefaultForUser(packageName, userId).isQuarantined();
+    public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException {
+        return getUserStateOrDefaultForUser(packageName, userId).isQuarantined();
     }
 
     @Override
-    public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) {
-        return getUserStageOrDefaultForUser(packageName, userId).isStopped();
+    public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException {
+        return getUserStateOrDefaultForUser(packageName, userId).isStopped();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index 24a33f1..e3bbd2d 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -951,20 +951,32 @@
     @Deprecated
     public final boolean isPackageSuspendedForUser(@NonNull String packageName,
             @UserIdInt int userId) {
-        return snapshot().isPackageSuspendedForUser(packageName, userId);
+        try {
+            return snapshot().isPackageSuspendedForUser(packageName, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Unknown target package: " + packageName);
+        }
     }
 
     @Override
     @Deprecated
     public final boolean isPackageQuarantinedForUser(@NonNull String packageName,
             @UserIdInt int userId) {
-        return snapshot().isPackageQuarantinedForUser(packageName, userId);
+        try {
+            return snapshot().isPackageQuarantinedForUser(packageName, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Unknown target package: " + packageName);
+        }
     }
 
     @Override
     public final boolean isPackageStoppedForUser(@NonNull String packageName,
             @UserIdInt int userId) {
-        return snapshot().isPackageStoppedForUser(packageName, userId);
+        try {
+            return snapshot().isPackageStoppedForUser(packageName, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Unknown target package: " + packageName);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 58c1c05..ba66377 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1575,6 +1575,30 @@
         }
 
         @Override
+        public List<String> getPreInstalledSystemPackages(UserHandle user) {
+            // Only system launchers, which have access to recents should have access to this API.
+            // TODO(b/303803157): Update access control for this API to default Launcher app.
+            if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
+                throw new SecurityException("Caller is not the recents app");
+            }
+            if (!canAccessProfile(user.getIdentifier(),
+                    "Can't access preinstalled packages for another user")) {
+                return null;
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                String userType = mUm.getUserInfo(user.getIdentifier()).userType;
+                Set<String> preInstalledPackages = mUm.getPreInstallableSystemPackages(userType);
+                if (preInstalledPackages == null) {
+                    return new ArrayList<>();
+                }
+                return List.copyOf(preInstalledPackages);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
                 String callingFeatureId, ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index e3bab3f..6b05edf 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -323,6 +323,7 @@
         PackageStateInternal ps = getPackageState(packageName, snapshot,
                 Binder.getCallingUid(), userId);
         verifyNotSystemApp(ps.getFlags());
+        verifyInstalled(ps, userId);
         String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
         verifyInstaller(responsibleInstallerPackage, userId);
         ApplicationInfo installerInfo = snapshot.getApplicationInfo(
@@ -476,6 +477,14 @@
         }
     }
 
+    private void verifyInstalled(PackageStateInternal ps, int userId)
+            throws PackageManager.NameNotFoundException {
+        if (!ps.getUserStateOrDefault(userId).isInstalled()) {
+            throw new PackageManager.NameNotFoundException(
+                    TextUtils.formatSimple("%s is not installed.", ps.getPackageName()));
+        }
+    }
+
     /**
      * Returns true if the app is archivable.
      */
@@ -519,11 +528,11 @@
     /**
      * Returns true if user has opted the app out of archiving through system settings.
      */
-    // TODO(b/304256918) Switch this to a separate OP code for archiving.
     private boolean isAppOptedOutOfArchiving(String packageName, int uid) {
         return Binder.withCleanCallingIdentity(() ->
-                getAppOpsManager().checkOp(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
-                        uid, packageName) == MODE_IGNORED);
+                getAppOpsManager().checkOpNoThrow(
+                        AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, packageName)
+                        == MODE_IGNORED);
     }
 
     private void verifyOptOutStatus(String packageName, int uid)
@@ -676,23 +685,51 @@
         PackageStateInternal ps;
         try {
             ps = getPackageState(packageName, snapshot, callingUid, userId);
-            snapshot.enforceCrossUserPermission(callingUid, userId, true, false,
-                    "getArchivedAppIcon");
-            verifyArchived(ps, userId);
         } catch (PackageManager.NameNotFoundException e) {
-            throw new ParcelableException(e);
+            Slog.e(TAG, TextUtils.formatSimple("Package %s couldn't be found.", packageName), e);
+            return null;
         }
 
-        List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault(
-                userId).getArchiveState().getActivityInfos();
-        if (activityInfos.size() == 0) {
+        ArchiveState archiveState = getAnyArchiveState(ps, userId);
+        if (archiveState == null || archiveState.getActivityInfos().size() == 0) {
             return null;
         }
 
         // TODO(b/298452477) Handle monochrome icons.
         // In the rare case the archived app defined more than two launcher activities, we choose
         // the first one arbitrarily.
-        return includeCloudOverlay(decodeIcon(activityInfos.get(0)));
+        return includeCloudOverlay(decodeIcon(archiveState.getActivityInfos().get(0)));
+    }
+
+    /**
+     * This method first checks the ArchiveState for the provided userId and then tries to fallback
+     * to other users if the current user is not archived.
+     *
+     * <p> This fallback behaviour is required for archived apps to fit into the multi-user world
+     * where APKs are shared across users. E.g. current ways of fetching icons for apps that are
+     * only installed on the work profile also work when executed on the personal profile if you're
+     * using {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}. Resource fetching from APKs is for
+     * the most part userId-agnostic, which we need to mimic here in order for existing methods
+     * like {@link PackageManager#getApplicationIcon} to continue working.
+     *
+     * @return {@link ArchiveState} for {@code userId} if present. If not present, false back to an
+     * arbitrary userId. If no user is archived, returns null.
+     */
+    @Nullable
+    private ArchiveState getAnyArchiveState(PackageStateInternal ps, int userId) {
+        PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+        if (isArchived(userState)) {
+            return userState.getArchiveState();
+        }
+
+        for (int i = 0; i < ps.getUserStates().size(); i++) {
+            userState = ps.getUserStates().valueAt(i);
+            if (isArchived(userState)) {
+                return userState.getArchiveState();
+            }
+        }
+
+        return null;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 1e7d043..c737b45 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -762,13 +762,14 @@
     }
 
     @Override
-    public boolean isPackageQuarantined(@NonNull String packageName,
-            @UserIdInt int userId) {
+    public boolean isPackageQuarantined(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException {
         return snapshot().isPackageQuarantinedForUser(packageName, userId);
     }
 
     @Override
-    public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) {
+    public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException {
         return snapshot().isPackageStoppedForUser(packageName, userId);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 84d4754..56365b6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5252,14 +5252,18 @@
 
         @Override
         public String getSuspendingPackage(String packageName, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final Computer snapshot = snapshot();
-            // This will do visibility checks as well.
-            if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
+            try {
+                final int callingUid = Binder.getCallingUid();
+                final Computer snapshot = snapshot();
+                // This will do visibility checks as well.
+                if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
+                    return null;
+                }
+                return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId,
+                        callingUid);
+            } catch (PackageManager.NameNotFoundException e) {
                 return null;
             }
-            return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId,
-                    callingUid);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a7b52f4..c48eccf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -532,9 +532,10 @@
             }
             final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT, android.content.IntentSender.class);
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
+            final String callingPackage = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
             // Call setQuietModeEnabled on bg thread to avoid ANR
             BackgroundThread.getHandler().post(() ->
-                    setQuietModeEnabled(userId, false, target, /* callingPackage */ null));
+                    setQuietModeEnabled(userId, false, target, callingPackage));
         }
     };
 
@@ -1410,7 +1411,7 @@
                     if (onlyIfCredentialNotRequired) {
                         return false;
                     }
-                    showConfirmCredentialToDisableQuietMode(userId, target);
+                    showConfirmCredentialToDisableQuietMode(userId, target, callingPackage);
                     return false;
                 }
             }
@@ -1434,7 +1435,7 @@
                 if (onlyIfCredentialNotRequired) {
                     return false;
                 }
-                showConfirmCredentialToDisableQuietMode(userId, target);
+                showConfirmCredentialToDisableQuietMode(userId, target, callingPackage);
                 return false;
             }
             setQuietModeEnabled(userId, false /* enableQuietMode */, target, callingPackage);
@@ -1604,7 +1605,7 @@
      * Show confirm credential screen to unlock user in order to turn off quiet mode.
      */
     private void showConfirmCredentialToDisableQuietMode(
-            @UserIdInt int userId, @Nullable IntentSender target) {
+            @UserIdInt int userId, @Nullable IntentSender target, @Nullable String callingPackage) {
         if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) {
             // TODO (b/308121702) It may be brittle to rely on user states to check profile state
             int state;
@@ -1635,6 +1636,7 @@
         }
         callBackIntent.putExtra(Intent.EXTRA_USER_ID, userId);
         callBackIntent.setPackage(mContext.getPackageName());
+        callBackIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackage);
         callBackIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 mContext,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fed32e5..938ed23 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -808,7 +808,10 @@
                     handleScreenShot(msg.arg1);
                     break;
                 case MSG_SWITCH_KEYBOARD_LAYOUT:
-                    handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
+                    SwitchKeyboardLayoutMessageObject object =
+                            (SwitchKeyboardLayoutMessageObject) msg.obj;
+                    handleSwitchKeyboardLayout(object.keyEvent, object.direction,
+                            object.focusedToken);
                     break;
                 case MSG_LOG_KEYBOARD_SYSTEM_EVENT:
                     handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj);
@@ -929,6 +932,10 @@
         }
     }
 
+    private record SwitchKeyboardLayoutMessageObject(KeyEvent keyEvent, IBinder focusedToken,
+                                                     int direction) {
+    }
+
     final IPersistentVrStateCallbacks mPersistentVrModeListener =
             new IPersistentVrStateCallbacks.Stub() {
         @Override
@@ -3641,7 +3648,7 @@
             case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
                 if (firstDown) {
                     int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
-                    sendSwitchKeyboardLayout(event, direction);
+                    sendSwitchKeyboardLayout(event, focusedToken, direction);
                     logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
                     return true;
                 }
@@ -3910,7 +3917,7 @@
                     + ", policyFlags=" + policyFlags);
         }
 
-        if (interceptUnhandledKey(event)) {
+        if (interceptUnhandledKey(event, focusedToken)) {
             return null;
         }
 
@@ -3968,7 +3975,7 @@
         return fallbackEvent;
     }
 
-    private boolean interceptUnhandledKey(KeyEvent event) {
+    private boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
         final int keyCode = event.getKeyCode();
         final int repeatCount = event.getRepeatCount();
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
@@ -3981,7 +3988,7 @@
                     if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
                             KeyEvent.META_CTRL_ON)) {
                         int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
-                        sendSwitchKeyboardLayout(event, direction);
+                        sendSwitchKeyboardLayout(event, focusedToken, direction);
                         return true;
                     }
                 }
@@ -4037,16 +4044,22 @@
         }
     }
 
-    private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) {
-        mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(),
-                direction).sendToTarget();
+    private void sendSwitchKeyboardLayout(@NonNull KeyEvent event,
+            @Nullable IBinder focusedToken, int direction) {
+        SwitchKeyboardLayoutMessageObject object =
+                new SwitchKeyboardLayoutMessageObject(event, focusedToken, direction);
+        mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget();
     }
 
-    private void handleSwitchKeyboardLayout(int deviceId, int direction) {
+    private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction,
+            IBinder focusedToken) {
         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
-            InputMethodManagerInternal.get().switchKeyboardLayout(direction);
+            IBinder targetWindowToken =
+                    mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
+            InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction,
+                    event.getDisplayId(), targetWindowToken);
         } else {
-            mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction);
+            mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
         }
     }
 
@@ -4056,7 +4069,7 @@
         if ((actions & ACTION_PASS_TO_USER) != 0) {
             long delayMillis = interceptKeyBeforeDispatching(
                     focusedToken, fallbackEvent, policyFlags);
-            if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent)) {
+            if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index eac4fc0..9a85c42 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1608,14 +1608,12 @@
                     user.name, user.id, user.flags);
             if (!user.supportsSwitchToByUser()) {
                 final boolean locked;
-                if (user.isProfile()) {
-                    if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) {
-                        fout.print(" (profile with separate challenge)");
-                        locked = isDeviceLockedInner(user.id);
-                    } else {
-                        fout.print(" (profile with unified challenge)");
-                        locked = isDeviceLockedInner(resolveProfileParent(user.id));
-                    }
+                if (mLockPatternUtils.isProfileWithUnifiedChallenge(user.id)) {
+                    fout.print(" (profile with unified challenge)");
+                    locked = isDeviceLockedInner(resolveProfileParent(user.id));
+                } else if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) {
+                    fout.print(" (profile with separate challenge)");
+                    locked = isDeviceLockedInner(user.id);
                 } else {
                     fout.println(" (user that cannot be switched to)");
                     locked = isDeviceLockedInner(user.id);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index 81e5fbd..769f01c 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -142,8 +142,11 @@
      *           if the launching activity is started from an existing launch sequence (trampoline)
      *           but cannot coalesce to the existing one, e.g. to a different display.
      * @param name The launching activity name.
+     * @param temperature The temperature at which a launch sequence had started.
+     * @param userId The id of the user the activity is being launched for.
      */
-    public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature) {
+    public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature,
+            int userId) {
     }
 
     /**
@@ -177,13 +180,15 @@
      * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
      *        To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
      *        from {@code timestampNanos}.
+     * @param launchMode The activity launch mode.
      *
      * @apiNote The finishing activity isn't necessarily the same as the starting activity;
      *          in the case of a trampoline, multiple activities could've been started
      *          and only the latest activity that was top-most during first-frame drawn
      *          is reported here.
      */
-    public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos) {
+    public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos,
+            int launchMode) {
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 7b20529..78f501a 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -1737,7 +1737,8 @@
 
         // Beginning a launch is timing sensitive and so should be observed as soon as possible.
         mLaunchObserver.onActivityLaunched(info.mLaunchingState.mStartUptimeNs,
-                info.mLastLaunchedActivity.mActivityComponent, temperature);
+                info.mLastLaunchedActivity.mActivityComponent, temperature,
+                info.mLastLaunchedActivity.mUserId);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -1774,7 +1775,8 @@
                 "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
 
         mLaunchObserver.onActivityLaunchFinished(info.mLaunchingState.mStartUptimeNs,
-                info.mLastLaunchedActivity.mActivityComponent, timestampNs);
+                info.mLastLaunchedActivity.mActivityComponent, timestampNs,
+                info.mLastLaunchedActivity.launchMode);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f8fda91..869bcc0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1275,7 +1275,6 @@
             @Nullable String callingFeatureId, Intent intent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
             ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
-
         final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
 
         assertPackageMatchesCallingUid(callingPackage);
@@ -1316,7 +1315,6 @@
                 .setActivityOptions(opts)
                 .setUserId(userId)
                 .execute();
-
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 92665af..4929df80 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 import static android.os.Process.SYSTEM_UID;
 import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
 
@@ -49,7 +50,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
@@ -86,7 +86,7 @@
     private static final int NO_PROCESS_UID = -1;
     /** If enabled the creator will not allow BAL on its behalf by default. */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
             296478951;
     public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
@@ -228,6 +228,7 @@
         private final Intent mIntent;
         private final WindowProcessController mCallerApp;
         private final WindowProcessController mRealCallerApp;
+        private final boolean mIsCallForResult;
 
         private BalState(int callingUid, int callingPid, final String callingPackage,
                  int realCallingUid, int realCallingPid,
@@ -247,8 +248,10 @@
             mOriginatingPendingIntent = originatingPendingIntent;
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
-            if (originatingPendingIntent == null // not a PendingIntent
-                    || resultRecord != null // sent for result
+            mIsCallForResult = resultRecord != null;
+            if (balRequireOptInByPendingIntentCreator() // auto-opt in introduced with this feature
+                    && (originatingPendingIntent == null // not a PendingIntent
+                    || mIsCallForResult) // sent for result
             ) {
                 // grant BAL privileges unless explicitly opted out
                 mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
@@ -351,6 +354,19 @@
             return name + "[debugOnly]";
         }
 
+        /** @return valid targetSdk or <code>-1</code> */
+        private int getTargetSdk(String packageName) {
+            if (packageName == null) {
+                return -1;
+            }
+            try {
+                PackageManager pm = mService.mContext.getPackageManager();
+                return pm.getTargetSdkVersion(packageName);
+            } catch (Exception e) {
+                return -1;
+            }
+        }
+
         private boolean hasRealCaller() {
             return mRealCallingUid != NO_PROCESS_UID;
         }
@@ -368,6 +384,7 @@
             StringBuilder sb = new StringBuilder(2048);
             sb.append("[callingPackage: ")
                     .append(getDebugPackageName(mCallingPackage, mCallingUid));
+            sb.append("; callingPackageTargetSdk: ").append(getTargetSdk(mCallingPackage));
             sb.append("; callingUid: ").append(mCallingUid);
             sb.append("; callingPid: ").append(mCallingPid);
             sb.append("; appSwitchState: ").append(mAppSwitchState);
@@ -387,10 +404,13 @@
                     .append(mBalAllowedByPiCreatorWithHardening);
             sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
             sb.append("; hasRealCaller: ").append(hasRealCaller());
+            sb.append("; isCallForResult: ").append(mIsCallForResult);
             sb.append("; isPendingIntent: ").append(isPendingIntent());
             if (hasRealCaller()) {
                 sb.append("; realCallingPackage: ")
                         .append(getDebugPackageName(mRealCallingPackage, mRealCallingUid));
+                sb.append("; realCallingPackageTargetSdk: ")
+                        .append(getTargetSdk(mRealCallingPackage));
                 sb.append("; realCallingUid: ").append(mRealCallingUid);
                 sb.append("; realCallingPid: ").append(mRealCallingPid);
                 sb.append("; realCallingUidHasAnyVisibleWindow: ")
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 4a3d0c1..32d60c5 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static com.android.input.flags.Flags.enablePointerChoreographer;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -23,6 +24,7 @@
 import android.annotation.NonNull;
 import android.content.ClipData;
 import android.content.Context;
+import android.hardware.input.InputManagerGlobal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -31,6 +33,8 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindow;
+import android.view.InputDevice;
+import android.view.PointerIcon;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -97,8 +101,8 @@
     }
 
     IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags,
-            SurfaceControl surface, int touchSource, float touchX, float touchY,
-            float thumbCenterX, float thumbCenterY, ClipData data) {
+            SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId,
+            float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
         if (DEBUG_DRAG) {
             Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
                     Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + ","
@@ -208,7 +212,17 @@
 
                 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
                 mDragState.broadcastDragStartedLocked(touchX, touchY);
-                mDragState.overridePointerIconLocked(touchSource);
+                if (enablePointerChoreographer()) {
+                    if ((touchSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+                        InputManagerGlobal.getInstance().setPointerIcon(
+                                PointerIcon.getSystemIcon(
+                                        mService.mContext, PointerIcon.TYPE_GRABBING),
+                                mDragState.mDisplayContent.getDisplayId(), touchDeviceId,
+                                touchPointerId, mDragState.getInputToken());
+                    }
+                } else {
+                    mDragState.overridePointerIconLocked(touchSource);
+                }
                 // remember the thumb offsets for later
                 mDragState.mThumbOffsetX = thumbCenterX;
                 mDragState.mThumbOffsetY = thumbCenterY;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a888f84..adbe3bc 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -416,6 +416,13 @@
         return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle;
     }
 
+    IBinder getInputToken() {
+        if (mInputInterceptor == null || mInputInterceptor.mClientChannel == null) {
+            return null;
+        }
+        return mInputInterceptor.mClientChannel.getToken();
+    }
+
     /**
      * @param display The Display that the window being dragged is on.
      */
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 9cbc1bd..4c73a41 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -84,10 +84,10 @@
     }
 
     @Override
-    public void onActivityLaunched(long id, ComponentName name, int temperature) {
+    public void onActivityLaunched(long id, ComponentName name, int temperature, int userId) {
         mHandler.sendMessage(PooledLambda.obtainMessage(
                 LaunchObserverRegistryImpl::handleOnActivityLaunched,
-                this, id, name, temperature));
+                this, id, name, temperature, userId));
     }
 
     @Override
@@ -97,10 +97,11 @@
     }
 
     @Override
-    public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs) {
+    public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs,
+            int launchMode) {
         mHandler.sendMessage(PooledLambda.obtainMessage(
                 LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
-                this, id, name, timestampNs));
+                this, id, name, timestampNs, launchMode));
     }
 
     @Override
@@ -137,10 +138,10 @@
     }
 
     private void handleOnActivityLaunched(long id, ComponentName name,
-            @Temperature int temperature) {
+            @Temperature int temperature, int userId) {
         // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
         for (int i = 0; i < mList.size(); i++) {
-            mList.get(i).onActivityLaunched(id, name, temperature);
+            mList.get(i).onActivityLaunched(id, name, temperature, userId);
         }
     }
 
@@ -151,10 +152,11 @@
         }
     }
 
-    private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs) {
+    private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs,
+            int launchMode) {
         // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
         for (int i = 0; i < mList.size(); i++) {
-            mList.get(i).onActivityLaunchFinished(id, name, timestampNs);
+            mList.get(i).onActivityLaunchFinished(id, name, timestampNs, launchMode);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 56f9aa4..57939bc 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -340,7 +340,8 @@
 
     @Override
     public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
-            float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
+            int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX,
+            float thumbCenterY, ClipData data) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         // Validate and resolve ClipDescription data before clearing the calling identity
@@ -349,7 +350,8 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
-                    touchX, touchY, thumbCenterX, thumbCenterY, data);
+                    touchDeviceId, touchPointerId, touchX, touchY, thumbCenterX, thumbCenterY,
+                    data);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 808a11d..516d37c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -866,6 +866,11 @@
     public abstract ImeTargetInfo onToggleImeRequested(boolean show,
             @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId);
 
+    /**
+     * Returns the token to identify the target window that the IME is associated with.
+     */
+    public abstract @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken);
+
     /** The information of input method target when IME is requested to show or hide. */
     public static class ImeTargetInfo {
         public final String focusedWindowName;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 10dd334..2125c63 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8552,6 +8552,12 @@
                         fromOrientations, toOrientations);
             }
         }
+
+        @Override
+        public @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken) {
+            InputTarget inputTarget = WindowManagerService.this.getInputTargetFromToken(inputToken);
+            return inputTarget == null ? null : inputTarget.getWindowToken();
+        }
     }
 
     private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 3d4f866..d66b9b9 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -182,7 +182,7 @@
 
     auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
     auto hash_block_count = block_count;
-    for (auto i = 0; hash_block_count > 1; i++) {
+    while (hash_block_count > 1) {
         hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
         total_tree_block_count += hash_block_count;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java
index 23886a1..00fe3d9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java
@@ -25,6 +25,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -167,8 +168,12 @@
     }
 
     private void setStoppedState(int uid, String pkgName, boolean stopped) {
-        doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid);
-        sendPackageStoppedBroadcast(uid, pkgName, stopped);
+        try {
+            doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid);
+            sendPackageStoppedBroadcast(uid, pkgName, stopped);
+        } catch (PackageManager.NameNotFoundException e) {
+            fail("Unable to set stopped state for unknown package: " + pkgName);
+        }
     }
 
     private void sendPackageStoppedBroadcast(int uid, String pkgName, boolean stopped) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 2332988..e5ecdc4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -62,6 +62,7 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.text.TextUtils;
+import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -174,6 +175,7 @@
         mUserState = new PackageUserStateImpl().setInstalled(true);
         mPackageSetting.setUserState(mUserId, mUserState);
         when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState);
+        when(mPackageState.getUserStates()).thenReturn(new SparseArray<>());
 
         when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
         when(mContext.getSystemService(AppOpsManager.class)).thenReturn(
@@ -343,7 +345,7 @@
 
     @Test
     public void archiveApp_appOptedOutOfArchiving() {
-        when(mAppOpsManager.checkOp(
+        when(mAppOpsManager.checkOpNoThrow(
                 eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED),
                 anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED);
 
@@ -430,7 +432,7 @@
     @Test
     public void isAppArchivable_appOptedOutOfArchiving()
             throws PackageManager.NameNotFoundException {
-        when(mAppOpsManager.checkOp(
+        when(mAppOpsManager.checkOpNoThrow(
                 eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED),
                 anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED);
 
@@ -551,22 +553,12 @@
         when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
                 null);
 
-        Exception e = assertThrows(
-                ParcelableException.class,
-                () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
-        assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
-        assertThat(e.getCause()).hasMessageThat().isEqualTo(
-                String.format("Package %s not found.", PACKAGE));
+        assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull();
     }
 
     @Test
     public void getArchivedAppIcon_notArchived() {
-        Exception e = assertThrows(
-                ParcelableException.class,
-                () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT));
-        assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
-        assertThat(e.getCause()).hasMessageThat().isEqualTo(
-                String.format("Package %s is not currently archived.", PACKAGE));
+        assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index a2a8424..0973d46 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -23,6 +23,7 @@
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
 import static com.android.server.hdmi.Constants.ADDR_RECORDER_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -1712,13 +1713,14 @@
                 HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
         HdmiCecMessage activeSourceFromTv =
                 HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
-
         mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
         mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        assertThat(mHdmiControlService.getLocalActiveSource()).isEqualTo(
+                new ActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS));
         mNativeWrapper.clearResultMessages();
         mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
         mTestLooper.dispatchAll();
@@ -1728,16 +1730,72 @@
         mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(activeSourceFromTv);
+        assertThat(mHdmiControlService.getLocalActiveSource()).isEqualTo(
+                new ActiveSource(mTvLogicalAddress, mTvPhysicalAddress));
     }
 
     @Test
+    public void requestActiveSourceActionComplete_validLocalActiveSource_doNotSendActiveSource() {
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mHdmiControlService.setActiveSource(mTvLogicalAddress, mTvPhysicalAddress,
+                "HdmiCecLocalDeviceTvTest");
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
+    }
+
+    @Test
+    public void onAddressAllocated_startRequestActiveSourceAction_cancelOnDeviceSelect() {
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(ADDR_PLAYBACK_1)
+                .setPhysicalAddress(0x1000)
+                .setPortId(PORT_1)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 1")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mNativeWrapper.clearResultMessages();
+        mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction should be cancelled and TV will not broadcast <Active Source>.
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
+    }
+
+
+    @Test
     public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() {
         mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
         assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
                 .isEmpty();
         HdmiCecMessage reportPhysicalAddress =
                 HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
-                ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+                        ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
         HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand(
                 ADDR_TV, ADDR_PLAYBACK_2);
         mNativeWrapper.onCecMessage(reportPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 18961c0..ee076c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -140,9 +140,9 @@
         }
 
         @Override
-        public ManagedProfilePasswordCache getManagedProfilePasswordCache(
+        public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
                 java.security.KeyStore ks) {
-            return mock(ManagedProfilePasswordCache.class);
+            return mock(UnifiedProfilePasswordCache.class);
         }
 
         @Override
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 6335b3b..4e1c72a 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -46,6 +46,7 @@
         "testng",
         "flag-junit",
         "notification_flags_lib",
+        "platform-test-rules",
     ],
 
     libs: [
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 36d55a4..14b551a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -211,6 +211,9 @@
 import android.permission.PermissionManager;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.rule.DeniedDevices;
+import android.platform.test.rule.DeviceProduct;
+import android.platform.test.rule.LimitDevicesRule;
 import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
@@ -281,6 +284,7 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -313,6 +317,7 @@
 @RunWith(AndroidTestingRunner.class)
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
 @RunWithLooper
+@DeniedDevices(denied = {DeviceProduct.CF_AUTO})
 public class NotificationManagerServiceTest extends UiServiceTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
     private static final String TEST_PACKAGE = "The.name.is.Package.Test.Package";
@@ -331,6 +336,9 @@
     private final int mUid = Binder.getCallingUid();
     private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
 
+    @ClassRule
+    public static final LimitDevicesRule sLimitDevicesRule = new LimitDevicesRule();
+
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 646ee3f..1aea56c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -80,6 +80,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.notNull;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -265,10 +266,12 @@
                 .thenReturn(CUSTOM_PKG_UID);
         when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
                 new String[] {pkg});
-        ApplicationInfo mockAppInfo = mock(ApplicationInfo.class);
-        when(mockAppInfo.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
+
+        ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
+        appInfoSpy.icon = ICON_RES_ID;
+        when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
         when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
-                .thenReturn(mockAppInfo);
+                .thenReturn(appInfoSpy);
         mZenModeHelper.mPm = mPackageManager;
 
         mZenModeEventLogger.reset();
@@ -3753,6 +3756,10 @@
         rule.zenPolicy = policy;
         rule.pkg = ownerPkg;
         rule.name = CUSTOM_APP_LABEL;
+        rule.iconResName = ICON_RES_NAME;
+        rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description,
+                CUSTOM_APP_LABEL);
+        rule.type = AutomaticZenRule.TYPE_OTHER;
         rule.enabled = true;
         return rule;
     }
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
index 656957c..159c760 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
@@ -120,12 +120,12 @@
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_currentAndPreinstalledAssistant_setsOp() {
+    public void setShouldReceiveSandboxedTrainingData_currentAndPreinstalledAssistant_setsOp() {
         // Set application info so current app is the current and preinstalled assistant.
         mApplicationInfo.uid = Process.myUid();
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
-        mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+        mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                 /* allowed= */ true);
 
         verify(mAppOpsManager).setUidMode(mOpIdCaptor.capture(), mUidCaptor.capture(),
@@ -137,7 +137,7 @@
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_missingPermission_doesNotSetOp() {
+    public void setShouldReceiveSandboxedTrainingData_missingPermission_doesNotSetOp() {
         // Set application info so current app is the current and preinstalled assistant.
         mApplicationInfo.uid = Process.myUid();
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
@@ -146,33 +146,33 @@
         mPermissionEnforcer.revoke(Manifest.permission.MANAGE_HOTWORD_DETECTION);
 
         assertThrows(SecurityException.class,
-                () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+                () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                         /* allowed= */ true));
 
         verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_notPreinstalledAssistant_doesNotSetOp() {
+    public void setShouldReceiveSandboxedTrainingData_notPreinstalledAssistant_doesNotSetOp() {
         // Set application info so current app is not preinstalled assistant.
         mApplicationInfo.uid = Process.myUid();
         mApplicationInfo.flags = ApplicationInfo.FLAG_INSTALLED; // Does not contain FLAG_SYSTEM.
 
         assertThrows(SecurityException.class,
-                () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+                () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                                 /* allowed= */ true));
 
         verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
     }
 
     @Test
-    public void setIsReceiveSandboxedTrainingDataAllowed_notCurrentAssistant_doesNotSetOp() {
+    public void setShouldReceiveSandboxedTrainingData_notCurrentAssistant_doesNotSetOp() {
         // Set application info so current app is not current assistant.
         mApplicationInfo.uid = Process.SHELL_UID; // Set current assistant uid to shell UID.
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
         assertThrows(SecurityException.class,
-                () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+                () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData(
                                 /* allowed= */ true));
 
         verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 8d236ed..0382ca0 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -61,6 +61,7 @@
         META_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
         META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING);
     }
+    private static final int ANY_DISPLAY_ID = 123;
 
     @Before
     public void setUp() {
@@ -96,8 +97,9 @@
      */
     @Test
     public void testCtrlSpace() {
-        sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, 0);
-        mPhoneWindowManager.assertSwitchKeyboardLayout(1);
+        sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, /* duration= */ 0,
+                ANY_DISPLAY_ID);
+        mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ 1, ANY_DISPLAY_ID);
     }
 
     /**
@@ -105,8 +107,9 @@
      */
     @Test
     public void testCtrlShiftSpace() {
-        sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 0);
-        mPhoneWindowManager.assertSwitchKeyboardLayout(-1);
+        sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE},
+                /* duration= */ 0, ANY_DISPLAY_ID);
+        mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ -1, ANY_DISPLAY_ID);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 9cdec25..157d162 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -114,7 +114,7 @@
         }
     }
 
-    void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress) {
+    void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress, int displayId) {
         final long downTime = mPhoneWindowManager.getCurrentTime();
         final int count = keyCodes.length;
         int metaState = 0;
@@ -124,7 +124,7 @@
             final KeyEvent event = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode,
                     0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
                     0 /*flags*/, InputDevice.SOURCE_KEYBOARD);
-            event.setDisplayId(DEFAULT_DISPLAY);
+            event.setDisplayId(displayId);
             interceptKey(event);
             // The order is important here, metaState could be updated and applied to the next key.
             metaState |= MODIFIER.getOrDefault(keyCode, 0);
@@ -142,7 +142,7 @@
                         KeyEvent.ACTION_DOWN, keyCode, 1 /*repeat*/, metaState,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
                         KeyEvent.FLAG_LONG_PRESS /*flags*/, InputDevice.SOURCE_KEYBOARD);
-                nextDownEvent.setDisplayId(DEFAULT_DISPLAY);
+                nextDownEvent.setDisplayId(displayId);
                 interceptKey(nextDownEvent);
             }
         }
@@ -153,18 +153,23 @@
             final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode,
                     0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
                     InputDevice.SOURCE_KEYBOARD);
-            upEvent.setDisplayId(DEFAULT_DISPLAY);
+            upEvent.setDisplayId(displayId);
             interceptKey(upEvent);
             metaState &= ~MODIFIER.getOrDefault(keyCode, 0);
         }
     }
 
     void sendKeyCombination(int[] keyCodes, long durationMillis) {
-        sendKeyCombination(keyCodes, durationMillis, false /* longPress */);
+        sendKeyCombination(keyCodes, durationMillis, false /* longPress */, DEFAULT_DISPLAY);
+    }
+
+    void sendKeyCombination(int[] keyCodes, long durationMillis, int displayId) {
+        sendKeyCombination(keyCodes, durationMillis, false /* longPress */, displayId);
     }
 
     void sendLongPressKeyCombination(int[] keyCodes) {
-        sendKeyCombination(keyCodes, ViewConfiguration.getLongPressTimeout(), true /* longPress */);
+        sendKeyCombination(keyCodes, ViewConfiguration.getLongPressTimeout(), true /* longPress */,
+                DEFAULT_DISPLAY);
     }
 
     void sendKey(int keyCode) {
@@ -172,7 +177,7 @@
     }
 
     void sendKey(int keyCode, boolean longPress) {
-        sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress);
+        sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY);
     }
 
     private void interceptKey(KeyEvent keyEvent) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 48d3503..0678210 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -166,6 +166,9 @@
     @Mock
     private PhoneWindowManager.ButtonOverridePermissionChecker mButtonOverridePermissionChecker;
 
+    @Mock private IBinder mInputToken;
+    @Mock private IBinder mImeTargetWindowToken;
+
     private StaticMockitoSession mMockitoSession;
     private OffsettableClock mClock = new OffsettableClock();
     private TestLooper mTestLooper = new TestLooper(() -> mClock.now());
@@ -327,6 +330,9 @@
         doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt(), anyInt());
         doNothing().when(mPhoneWindowManager).lockNow(any());
 
+        doReturn(mImeTargetWindowToken)
+                .when(mWindowManagerInternal).getTargetWindowTokenFromInputToken(mInputToken);
+
         mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl));
         mPhoneWindowManager.systemReady();
         mPhoneWindowManager.systemBooted();
@@ -365,12 +371,12 @@
     }
 
     long interceptKeyBeforeDispatching(KeyEvent event) {
-        return mPhoneWindowManager.interceptKeyBeforeDispatching(null /*focusedToken*/,
-                event, FLAG_INTERACTIVE);
+        return mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, event,
+                FLAG_INTERACTIVE);
     }
 
     void dispatchUnhandledKey(KeyEvent event) {
-        mPhoneWindowManager.dispatchUnhandledKey(null /*focusedToken*/, event, FLAG_INTERACTIVE);
+        mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE);
     }
 
     long getCurrentTime() {
@@ -646,14 +652,16 @@
         verify(mStatusBarManagerInternal).startAssist(any());
     }
 
-    void assertSwitchKeyboardLayout(int direction) {
+    void assertSwitchKeyboardLayout(int direction, int displayId) {
         mTestLooper.dispatchAll();
         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
-            verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction));
+            verify(mInputMethodManagerInternal).onSwitchKeyboardLayoutShortcut(eq(direction),
+                    eq(displayId), eq(mImeTargetWindowToken));
             verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt());
         } else {
             verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction));
-            verify(mInputMethodManagerInternal, never()).switchKeyboardLayout(anyInt());
+            verify(mInputMethodManagerInternal, never())
+                    .onSwitchKeyboardLayoutShortcut(anyInt(), anyInt(), any());
         }
     }
 
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 65e77dc..d4ba3b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -124,7 +124,7 @@
     private void verifyOnActivityLaunched(ActivityRecord activity) {
         final ArgumentCaptor<Long> idCaptor = ArgumentCaptor.forClass(Long.class);
         verifyAsync(mLaunchObserver).onActivityLaunched(idCaptor.capture(),
-                eq(activity.mActivityComponent), anyInt());
+                eq(activity.mActivityComponent), anyInt(), anyInt());
         final long id = idCaptor.getValue();
         setExpectedStartedId(id, activity);
         mLastLaunchedIds.put(activity.mActivityComponent, id);
@@ -132,7 +132,7 @@
 
     private void verifyOnActivityLaunchFinished(ActivityRecord activity) {
         verifyAsync(mLaunchObserver).onActivityLaunchFinished(eq(mExpectedStartedId),
-                eq(activity.mActivityComponent), anyLong());
+                eq(activity.mActivityComponent), anyLong(), anyInt());
     }
 
     private void setExpectedStartedId(long id, Object reason) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 50fe042..1fb7cd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -553,7 +553,7 @@
             assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
                     new InputChannel(), true /* isDragDrop */));
             mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
-                    flag, surface, 0, 0, 0, 0, 0, data);
+                    flag, surface, 0, 0, 0, 0, 0, 0, 0, data);
             assertNotNull(mToken);
 
             r.run();
@@ -575,7 +575,7 @@
 
     private void startA11yDrag(int flags, ClipData data, Runnable r) {
         mToken = mTarget.performDrag(0, 0, mWindow.mClient,
-                flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, data);
+                flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, 0, 0, data);
         assertNotNull(mToken);
         r.run();
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 6e4f13a..c902d459 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1569,13 +1569,13 @@
 
         @Override
         @EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
-        public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) {
-            super.setIsReceiveSandboxedTrainingDataAllowed_enforcePermission();
+        public void setShouldReceiveSandboxedTrainingData(boolean allowed) {
+            super.setShouldReceiveSandboxedTrainingData_enforcePermission();
 
             synchronized (this) {
                 if (mImpl == null) {
                     throw new IllegalStateException(
-                            "setIsReceiveSandboxedTrainingDataAllowed without running voice "
+                            "setShouldReceiveSandboxedTrainingData without running voice "
                                     + "interaction service");
                 }