Merge "Fix tap outside focus for SCVH from different process." into udc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 887ee5f..644d92c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -33,6 +33,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.app.compat.CompatChanges;
 import android.app.job.IJobScheduler;
 import android.app.job.IUserVisibleJobObserver;
@@ -1250,7 +1251,7 @@
         return pkg;
     }
 
-    final private IUidObserver mUidObserver = new IUidObserver.Stub() {
+    final private IUidObserver mUidObserver = new UidObserver() {
         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
                 int capability) {
             final SomeArgs args = SomeArgs.obtain();
@@ -1264,19 +1265,13 @@
             mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
         }
 
-        @Override public void onUidActive(int uid) throws RemoteException {
+        @Override public void onUidActive(int uid) {
             mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
         }
 
         @Override public void onUidIdle(int uid, boolean disabled) {
             mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
         }
-
-        @Override public void onUidCachedChanged(int uid, boolean cached) {
-        }
-
-        @Override public void onUidProcAdjChanged(int uid) {
-        }
     };
 
     public Context getTestableContext() {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 7aedd30..96d785f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4067,8 +4067,9 @@
          * notification if alerts for this notification's group should be handled by a different
          * notification. This is only applicable for notifications that belong to a
          * {@link #setGroup(String) group}. This must be called on all notifications you want to
-         * mute. For example, if you want only the summary of your group to make noise, all
-         * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}.
+         * mute. For example, if you want only the summary of your group to make noise and/or peek
+         * on screen, all children in the group should have the group alert behavior
+         * {@link #GROUP_ALERT_SUMMARY}.
          *
          * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
          */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bad6c77..7dabe60 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6413,7 +6413,7 @@
     public void lockNow(@LockNowFlag int flags) {
         if (mService != null) {
             try {
-                mService.lockNow(flags, mParentInstance);
+                mService.lockNow(flags, mContext.getPackageName(), mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -13646,8 +13646,8 @@
      * privacy-sensitive events happening outside the managed profile would have been redacted
      * already.
      *
-     * @param admin Which device admin this request is associated with. Null if the caller is not
-     *              a device admin
+     * @param admin Which device admin this request is associated with, or {@code null}
+     *              if called by a delegated app.
      * @param enabled whether security logging should be enabled or not.
      * @throws SecurityException if the caller is not permitted to control security logging.
      * @see #setAffiliationIds
@@ -13699,8 +13699,8 @@
      * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown.
      * See {@link #isAffiliatedUser}.
      *
-     * @param admin Which device admin this request is associated with. Null if the caller is not
-     *              a device admin.
+     * @param admin Which device admin this request is associated with, or {@code null}
+     *              if called by a delegated app.
      * @return the new batch of security logs which is a list of {@link SecurityEvent},
      * or {@code null} if rate limitation is exceeded or if logging is currently disabled.
      * @throws SecurityException if the caller is not allowed to access security logging,
@@ -13857,8 +13857,8 @@
      * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown.
      * See {@link #isAffiliatedUser}.
      *
-     * @param admin Which device admin this request is associated with. Null if the caller is not
-     *              a device admin.
+     * @param admin Which device admin this request is associated with, or {@code null}
+     *             if called by a delegated app.
      * @return Device logs from before the latest reboot of the system, or {@code null} if this API
      *         is not supported on the device.
      * @throws SecurityException if the caller is not allowed to access security logging, or
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index a498913..593f736 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -1857,6 +1857,17 @@
             public static final String WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON =
                     PREFIX + "TURN_ON_WORK_PROFILE_BUTTON_TEXT";
 
+            public static final String MINIRESOLVER_OPEN_IN_WORK =
+                    PREFIX + "MINIRESOLVER_OPEN_IN_WORK";
+
+            public static final String MINIRESOLVER_OPEN_IN_PERSONAL =
+                    PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL";
+
+            public static final String MINIRESOLVER_USE_WORK_BROWSER =
+                    PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL";
+
+            public static final String MINIRESOLVER_USE_PERSONAL_BROWSER =
+                    PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL";
         }
 
         /**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8d508c0..9b0b18a 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -119,7 +119,7 @@
     void setRequiredStrongAuthTimeout(in ComponentName who, String callerPackageName, long timeMs, boolean parent);
     long getRequiredStrongAuthTimeout(in ComponentName who, int userId, boolean parent);
 
-    void lockNow(int flags, boolean parent);
+    void lockNow(int flags, String callerPackageName, boolean parent);
 
     /**
     * @param factoryReset only applicable when `targetSdk >= U`, either tries to factoryReset/fail or removeUser/fail otherwise
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index da6784b..2ca2b79bc 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -64,6 +64,7 @@
 import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.IntConsumer;
 
@@ -171,6 +172,7 @@
     public VirtualDevice createVirtualDevice(
             int associationId,
             @NonNull VirtualDeviceParams params) {
+        Objects.requireNonNull(params, "params must not be null");
         try {
             return new VirtualDevice(mService, mContext, associationId, params);
         } catch (RemoteException e) {
@@ -409,6 +411,9 @@
                 @NonNull PendingIntent pendingIntent,
                 @NonNull Executor executor,
                 @NonNull IntConsumer listener) {
+            Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
+            Objects.requireNonNull(executor, "executor must not be null");
+            Objects.requireNonNull(listener, "listener must not be null");
             mVirtualDeviceInternal.launchPendingIntent(
                     displayId, pendingIntent, executor, listener);
         }
@@ -483,6 +488,7 @@
                 @NonNull VirtualDisplayConfig config,
                 @Nullable @CallbackExecutor Executor executor,
                 @Nullable VirtualDisplay.Callback callback) {
+            Objects.requireNonNull(config, "config must not be null");
             return mVirtualDeviceInternal.createVirtualDisplay(config, executor, callback);
         }
 
@@ -503,6 +509,7 @@
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
+            Objects.requireNonNull(config, "config must not be null");
             return mVirtualDeviceInternal.createVirtualDpad(config);
         }
 
@@ -514,6 +521,7 @@
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) {
+            Objects.requireNonNull(config, "config must not be null");
             return mVirtualDeviceInternal.createVirtualKeyboard(config);
         }
 
@@ -550,6 +558,7 @@
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) {
+            Objects.requireNonNull(config, "config must not be null");
             return mVirtualDeviceInternal.createVirtualMouse(config);
         }
 
@@ -587,6 +596,7 @@
         @NonNull
         public VirtualTouchscreen createVirtualTouchscreen(
                 @NonNull VirtualTouchscreenConfig config) {
+            Objects.requireNonNull(config, "config must not be null");
             return mVirtualDeviceInternal.createVirtualTouchscreen(config);
         }
 
@@ -659,6 +669,7 @@
                 @NonNull VirtualDisplay display,
                 @Nullable Executor executor,
                 @Nullable AudioConfigurationChangeCallback callback) {
+            Objects.requireNonNull(display, "display must not be null");
             return mVirtualDeviceInternal.createVirtualAudioDevice(display, executor, callback);
         }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 58b0571..154068e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4233,6 +4233,13 @@
             "com.android.intent.action.SHOW_BRIGHTNESS_DIALOG";
 
     /**
+     * Activity Action: Shows the contrast setting dialog.
+     * @hide
+     */
+    public static final String ACTION_SHOW_CONTRAST_DIALOG =
+            "com.android.intent.action.SHOW_CONTRAST_DIALOG";
+
+    /**
      * Broadcast Action:  A global button was pressed.  Includes a single
      * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
      * caused the broadcast.
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 00ce17a..9140d02 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -460,9 +460,17 @@
         return false;
     }
 
+    /**
+     * Returns whether the service is enabled.
+     *
+     * @hide
+     */
     private boolean isServiceEnabled() {
-        return DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, true);
+        try {
+            return mService.isServiceEnabled();
+        } catch (RemoteException e) {
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 5fde96b..b779c56 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -58,5 +58,7 @@
     List<CredentialProviderInfo> getCredentialProviderServices(in int userId, in int providerFilter);
 
     List<CredentialProviderInfo> getCredentialProviderServicesForTesting(in int providerFilter);
+
+    boolean isServiceEnabled();
 }
 
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b5281a5..2aead3c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1758,10 +1758,12 @@
         /**
          * Key for the brightness throttling data as a String formatted:
          * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>]
-         * Where the latter part is repeated for each throttling level, and the entirety is repeated
-         * for each display, separated by a semicolon.
+         * [,<throttlingId>]?
+         * Where [<severity as string>,<brightness cap>] is repeated for each throttling level.
+         * The entirety is repeated for each display and throttling id, separated by a semicolon.
          * For example:
          * 123,1,critical,0.8;456,2,moderate,0.9,critical,0.7
+         * 123,1,critical,0.8,default;123,1,moderate,0.6,id_2;456,2,moderate,0.9,critical,0.7
          */
         String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
     }
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 70bd504..15ed450 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -26,13 +26,17 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.util.Slog;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 
 import com.android.internal.policy.PhoneWindow;
 
+import java.util.Objects;
+
 /**
  * Window of type {@code LayoutParams.TYPE_INPUT_METHOD_DIALOG} for drawing
  * Handwriting Ink on screen.
@@ -185,4 +189,12 @@
         return getDecorView().getVisibility() == View.VISIBLE
                 && mInkView != null && mInkView.isVisibleToUser();
     }
+
+    void dispatchHandwritingEvent(@NonNull MotionEvent event) {
+        final View decor = getDecorView();
+        Objects.requireNonNull(decor);
+        final ViewRootImpl viewRoot = decor.getViewRootImpl();
+        Objects.requireNonNull(viewRoot);
+        viewRoot.enqueueInputEvent(event);
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ee9d8a4..a9c4818 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2563,7 +2563,7 @@
      */
     public void onStylusHandwritingMotionEvent(@NonNull MotionEvent motionEvent) {
         if (mInkWindow != null && mInkWindow.isInkViewVisible()) {
-            mInkWindow.getDecorView().dispatchTouchEvent(motionEvent);
+            mInkWindow.dispatchHandwritingEvent(motionEvent);
         } else {
             if (mPendingEvents == null) {
                 mPendingEvents = new RingBuffer(MotionEvent.class, MAX_EVENTS_BUFFER);
@@ -2576,7 +2576,7 @@
                             if (mInkWindow == null) {
                                 break;
                             }
-                            mInkWindow.getDecorView().dispatchTouchEvent(event);
+                            mInkWindow.dispatchHandwritingEvent(event);
                         }
                         mPendingEvents.clear();
                     }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 5c2b389..402da28 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1008,9 +1008,8 @@
                 .allowAlarms(allowAlarms)
                 .allowMedia(allowMedia)
                 .allowSystem(allowSystem)
-                .allowConversations(allowConversations
-                        ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom)
-                        : ZenPolicy.PEOPLE_TYPE_NONE);
+                .allowConversations(allowConversations ? allowConversationsFrom
+                        : ZenPolicy.CONVERSATION_SENDERS_NONE);
         if (suppressedVisualEffects == 0) {
             builder.showAllVisualEffects();
         } else {
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index eb467e0..992a586 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -75,6 +75,11 @@
     private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
 
     /**
+     * The maximum number of signers supported by the v2 APK signature scheme.
+     */
+    private static final int MAX_V2_SIGNERS = 10;
+
+    /**
      * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature.
      *
      * <p><b>NOTE: This method does not verify the signature.</b>
@@ -183,6 +188,11 @@
         }
         while (signers.hasRemaining()) {
             signerCount++;
+            if (signerCount > MAX_V2_SIGNERS) {
+                throw new SecurityException(
+                        "APK Signature Scheme v2 only supports a maximum of " + MAX_V2_SIGNERS
+                                + " signers");
+            }
             try {
                 ByteBuffer signer = getLengthPrefixedSlice(signers);
                 X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory);
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index 4525490..a6aca33 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -78,6 +78,11 @@
         "SHA1",
     };
 
+    /**
+     * The maximum number of signers supported by the JAR signature scheme.
+     */
+    private static final int MAX_JAR_SIGNERS = 10;
+
     private final String jarName;
     private final StrictJarManifest manifest;
     private final HashMap<String, byte[]> metaEntries;
@@ -293,10 +298,16 @@
             return false;
         }
 
+        int signerCount = 0;
         Iterator<String> it = metaEntries.keySet().iterator();
         while (it.hasNext()) {
             String key = it.next();
             if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) {
+                if (++signerCount > MAX_JAR_SIGNERS) {
+                    throw new SecurityException(
+                            "APK Signature Scheme v1 only supports a maximum of " + MAX_JAR_SIGNERS
+                                    + " signers");
+                }
                 verifyCertificate(key);
                 it.remove();
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 152fa08..2f5cd54 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -265,6 +265,7 @@
     private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV;
     private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV;
     private static final boolean DEBUG_SCROLL_CAPTURE = false || LOCAL_LOGV;
+    private static final boolean DEBUG_TOUCH_NAVIGATION = false || LOCAL_LOGV;
     private static final boolean DEBUG_BLAST = false || LOCAL_LOGV;
     private static final int LOGTAG_INPUT_FOCUS = 62001;
 
@@ -7122,7 +7123,8 @@
                         mJoystick.cancel();
                     } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
                             == InputDevice.SOURCE_TOUCH_NAVIGATION) {
-                        mTouchNavigation.cancel(event);
+                        // Touch navigation events cannot be cancelled since they are dispatched
+                        // immediately.
                     }
                 }
             }
@@ -7641,392 +7643,109 @@
     }
 
     /**
-     * Creates dpad events from unhandled touch navigation movements.
+     * Creates DPAD events from unhandled touch navigation movements.
      */
     final class SyntheticTouchNavigationHandler extends Handler {
         private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler";
-        private static final boolean LOCAL_DEBUG = false;
 
-        // Assumed nominal width and height in millimeters of a touch navigation pad,
-        // if no resolution information is available from the input system.
-        private static final float DEFAULT_WIDTH_MILLIMETERS = 48;
-        private static final float DEFAULT_HEIGHT_MILLIMETERS = 48;
-
-        /* TODO: These constants should eventually be moved to ViewConfiguration. */
-
-        // The nominal distance traveled to move by one unit.
-        private static final int TICK_DISTANCE_MILLIMETERS = 12;
-
-        // Minimum and maximum fling velocity in ticks per second.
-        // The minimum velocity should be set such that we perform enough ticks per
-        // second that the fling appears to be fluid.  For example, if we set the minimum
-        // to 2 ticks per second, then there may be up to half a second delay between the next
-        // to last and last ticks which is noticeably discrete and jerky.  This value should
-        // probably not be set to anything less than about 4.
-        // If fling accuracy is a problem then consider tuning the tick distance instead.
-        private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f;
-        private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f;
-
-        // Fling velocity decay factor applied after each new key is emitted.
-        // This parameter controls the deceleration and overall duration of the fling.
-        // The fling stops automatically when its velocity drops below the minimum
-        // fling velocity defined above.
-        private static final float FLING_TICK_DECAY = 0.8f;
-
-        /* The input device that we are tracking. */
-
+        // The id of the input device that is being tracked.
         private int mCurrentDeviceId = -1;
         private int mCurrentSource;
-        private boolean mCurrentDeviceSupported;
 
-        /* Configuration for the current input device. */
-
-        // The scaled tick distance.  A movement of this amount should generally translate
-        // into a single dpad event in a given direction.
-        private float mConfigTickDistance;
-
-        // The minimum and maximum scaled fling velocity.
-        private float mConfigMinFlingVelocity;
-        private float mConfigMaxFlingVelocity;
-
-        /* Tracking state. */
-
-        // The velocity tracker for detecting flings.
-        private VelocityTracker mVelocityTracker;
-
-        // The active pointer id, or -1 if none.
-        private int mActivePointerId = -1;
-
-        // Location where tracking started.
-        private float mStartX;
-        private float mStartY;
-
-        // Most recently observed position.
-        private float mLastX;
-        private float mLastY;
-
-        // Accumulated movement delta since the last direction key was sent.
-        private float mAccumulatedX;
-        private float mAccumulatedY;
-
-        // Set to true if any movement was delivered to the app.
-        // Implies that tap slop was exceeded.
-        private boolean mConsumedMovement;
-
-        // The most recently sent key down event.
-        // The keycode remains set until the direction changes or a fling ends
-        // so that repeated key events may be generated as required.
-        private long mPendingKeyDownTime;
-        private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
-        private int mPendingKeyRepeatCount;
         private int mPendingKeyMetaState;
 
-        // The current fling velocity while a fling is in progress.
-        private boolean mFlinging;
-        private float mFlingVelocity;
+        private final GestureDetector mGestureDetector = new GestureDetector(mContext,
+                new GestureDetector.OnGestureListener() {
+                    @Override
+                    public boolean onDown(@NonNull MotionEvent e) {
+                        // This can be ignored since it's not clear what KeyEvent this will
+                        // belong to.
+                        return true;
+                    }
 
-        public SyntheticTouchNavigationHandler() {
+                    @Override
+                    public void onShowPress(@NonNull MotionEvent e) {
+
+                    }
+
+                    @Override
+                    public boolean onSingleTapUp(@NonNull MotionEvent e) {
+                        dispatchTap(e.getEventTime());
+                        return true;
+                    }
+
+                    @Override
+                    public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+                            float distanceX, float distanceY) {
+                        // Scroll doesn't translate to DPAD events so should be ignored.
+                        return true;
+                    }
+
+                    @Override
+                    public void onLongPress(@NonNull MotionEvent e) {
+                        // Long presses don't translate to DPAD events so should be ignored.
+                    }
+
+                    @Override
+                    public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+                            float velocityX, float velocityY) {
+                        dispatchFling(velocityX, velocityY, e2.getEventTime());
+                        return true;
+                    }
+                });
+
+        SyntheticTouchNavigationHandler() {
             super(true);
         }
 
         public void process(MotionEvent event) {
+            if (event.getDevice() == null) {
+                // The current device is not supported.
+                if (DEBUG_TOUCH_NAVIGATION) {
+                    Log.d(LOCAL_TAG,
+                            "Current device not supported so motion event is not processed");
+                }
+                return;
+            }
+            mPendingKeyMetaState = event.getMetaState();
             // Update the current device information.
-            final long time = event.getEventTime();
             final int deviceId = event.getDeviceId();
             final int source = event.getSource();
             if (mCurrentDeviceId != deviceId || mCurrentSource != source) {
-                finishKeys(time);
-                finishTracking(time);
                 mCurrentDeviceId = deviceId;
                 mCurrentSource = source;
-                mCurrentDeviceSupported = false;
-                InputDevice device = event.getDevice();
-                if (device != null) {
-                    // In order to support an input device, we must know certain
-                    // characteristics about it, such as its size and resolution.
-                    InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X);
-                    InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y);
-                    if (xRange != null && yRange != null) {
-                        mCurrentDeviceSupported = true;
-
-                        // Infer the resolution if it not actually known.
-                        float xRes = xRange.getResolution();
-                        if (xRes <= 0) {
-                            xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS;
-                        }
-                        float yRes = yRange.getResolution();
-                        if (yRes <= 0) {
-                            yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS;
-                        }
-                        float nominalRes = (xRes + yRes) * 0.5f;
-
-                        // Precompute all of the configuration thresholds we will need.
-                        mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes;
-                        mConfigMinFlingVelocity =
-                                MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
-                        mConfigMaxFlingVelocity =
-                                MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
-
-                        if (LOCAL_DEBUG) {
-                            Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
-                                    + " (" + Integer.toHexString(mCurrentSource) + "): "
-                                    + ", mConfigTickDistance=" + mConfigTickDistance
-                                    + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity
-                                    + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity);
-                        }
-                    }
-                }
-            }
-            if (!mCurrentDeviceSupported) {
-                return;
             }
 
-            // Handle the event.
-            final int action = event.getActionMasked();
-            switch (action) {
-                case MotionEvent.ACTION_DOWN: {
-                    boolean caughtFling = mFlinging;
-                    finishKeys(time);
-                    finishTracking(time);
-                    mActivePointerId = event.getPointerId(0);
-                    mVelocityTracker = VelocityTracker.obtain();
-                    mVelocityTracker.addMovement(event);
-                    mStartX = event.getX();
-                    mStartY = event.getY();
-                    mLastX = mStartX;
-                    mLastY = mStartY;
-                    mAccumulatedX = 0;
-                    mAccumulatedY = 0;
-
-                    // If we caught a fling, then pretend that the tap slop has already
-                    // been exceeded to suppress taps whose only purpose is to stop the fling.
-                    mConsumedMovement = caughtFling;
-                    break;
-                }
-
-                case MotionEvent.ACTION_MOVE:
-                case MotionEvent.ACTION_UP: {
-                    if (mActivePointerId < 0) {
-                        break;
-                    }
-                    final int index = event.findPointerIndex(mActivePointerId);
-                    if (index < 0) {
-                        finishKeys(time);
-                        finishTracking(time);
-                        break;
-                    }
-
-                    mVelocityTracker.addMovement(event);
-                    final float x = event.getX(index);
-                    final float y = event.getY(index);
-                    mAccumulatedX += x - mLastX;
-                    mAccumulatedY += y - mLastY;
-                    mLastX = x;
-                    mLastY = y;
-
-                    // Consume any accumulated movement so far.
-                    final int metaState = event.getMetaState();
-                    consumeAccumulatedMovement(time, metaState);
-
-                    // Detect taps and flings.
-                    if (action == MotionEvent.ACTION_UP) {
-                        if (mConsumedMovement && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
-                            // It might be a fling.
-                            mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity);
-                            final float vx = mVelocityTracker.getXVelocity(mActivePointerId);
-                            final float vy = mVelocityTracker.getYVelocity(mActivePointerId);
-                            if (!startFling(time, vx, vy)) {
-                                finishKeys(time);
-                            }
-                        }
-                        finishTracking(time);
-                    }
-                    break;
-                }
-
-                case MotionEvent.ACTION_CANCEL: {
-                    finishKeys(time);
-                    finishTracking(time);
-                    break;
-                }
-            }
+            // Interpret the event.
+            mGestureDetector.onTouchEvent(event);
         }
 
-        public void cancel(MotionEvent event) {
-            if (mCurrentDeviceId == event.getDeviceId()
-                    && mCurrentSource == event.getSource()) {
-                final long time = event.getEventTime();
-                finishKeys(time);
-                finishTracking(time);
-            }
+        private void dispatchTap(long time) {
+            dispatchEvent(time, KeyEvent.KEYCODE_DPAD_CENTER);
         }
 
-        private void finishKeys(long time) {
-            cancelFling();
-            sendKeyUp(time);
-        }
-
-        private void finishTracking(long time) {
-            if (mActivePointerId >= 0) {
-                mActivePointerId = -1;
-                mVelocityTracker.recycle();
-                mVelocityTracker = null;
-            }
-        }
-
-        private void consumeAccumulatedMovement(long time, int metaState) {
-            final float absX = Math.abs(mAccumulatedX);
-            final float absY = Math.abs(mAccumulatedY);
-            if (absX >= absY) {
-                if (absX >= mConfigTickDistance) {
-                    mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX,
-                            KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT);
-                    mAccumulatedY = 0;
-                    mConsumedMovement = true;
-                }
+        private void dispatchFling(float x, float y, long time) {
+            if (Math.abs(x) > Math.abs(y)) {
+                dispatchEvent(time,
+                        x > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT);
             } else {
-                if (absY >= mConfigTickDistance) {
-                    mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY,
-                            KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN);
-                    mAccumulatedX = 0;
-                    mConsumedMovement = true;
-                }
+                dispatchEvent(time, y > 0 ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP);
             }
         }
 
-        private float consumeAccumulatedMovement(long time, int metaState,
-                float accumulator, int negativeKeyCode, int positiveKeyCode) {
-            while (accumulator <= -mConfigTickDistance) {
-                sendKeyDownOrRepeat(time, negativeKeyCode, metaState);
-                accumulator += mConfigTickDistance;
+        private void dispatchEvent(long time, int keyCode) {
+            if (DEBUG_TOUCH_NAVIGATION) {
+                Log.d(LOCAL_TAG, "Dispatching DPAD events DOWN and UP with keycode " + keyCode);
             }
-            while (accumulator >= mConfigTickDistance) {
-                sendKeyDownOrRepeat(time, positiveKeyCode, metaState);
-                accumulator -= mConfigTickDistance;
-            }
-            return accumulator;
+            enqueueInputEvent(new KeyEvent(time, time,
+                    KeyEvent.ACTION_DOWN, keyCode, /* repeat= */ 0, mPendingKeyMetaState,
+                    mCurrentDeviceId, /* scancode= */ 0, KeyEvent.FLAG_FALLBACK,
+                    mCurrentSource));
+            enqueueInputEvent(new KeyEvent(time, time,
+                    KeyEvent.ACTION_UP, keyCode, /* repeat= */ 0, mPendingKeyMetaState,
+                    mCurrentDeviceId, /* scancode= */ 0, KeyEvent.FLAG_FALLBACK,
+                    mCurrentSource));
         }
-
-        private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) {
-            if (mPendingKeyCode != keyCode) {
-                sendKeyUp(time);
-                mPendingKeyDownTime = time;
-                mPendingKeyCode = keyCode;
-                mPendingKeyRepeatCount = 0;
-            } else {
-                mPendingKeyRepeatCount += 1;
-            }
-            mPendingKeyMetaState = metaState;
-
-            // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1
-            // but it doesn't quite make sense when simulating the events in this way.
-            if (LOCAL_DEBUG) {
-                Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode
-                        + ", repeatCount=" + mPendingKeyRepeatCount
-                        + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
-            }
-            enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
-                    KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount,
-                    mPendingKeyMetaState, mCurrentDeviceId,
-                    KeyEvent.FLAG_FALLBACK, mCurrentSource));
-        }
-
-        private void sendKeyUp(long time) {
-            if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
-                if (LOCAL_DEBUG) {
-                    Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode
-                            + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
-                }
-                enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
-                        KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState,
-                        mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK,
-                        mCurrentSource));
-                mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
-            }
-        }
-
-        private boolean startFling(long time, float vx, float vy) {
-            if (LOCAL_DEBUG) {
-                Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy
-                        + ", min=" + mConfigMinFlingVelocity);
-            }
-
-            // Flings must be oriented in the same direction as the preceding movements.
-            switch (mPendingKeyCode) {
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                    if (-vx >= mConfigMinFlingVelocity
-                            && Math.abs(vy) < mConfigMinFlingVelocity) {
-                        mFlingVelocity = -vx;
-                        break;
-                    }
-                    return false;
-
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    if (vx >= mConfigMinFlingVelocity
-                            && Math.abs(vy) < mConfigMinFlingVelocity) {
-                        mFlingVelocity = vx;
-                        break;
-                    }
-                    return false;
-
-                case KeyEvent.KEYCODE_DPAD_UP:
-                    if (-vy >= mConfigMinFlingVelocity
-                            && Math.abs(vx) < mConfigMinFlingVelocity) {
-                        mFlingVelocity = -vy;
-                        break;
-                    }
-                    return false;
-
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                    if (vy >= mConfigMinFlingVelocity
-                            && Math.abs(vx) < mConfigMinFlingVelocity) {
-                        mFlingVelocity = vy;
-                        break;
-                    }
-                    return false;
-            }
-
-            // Post the first fling event.
-            mFlinging = postFling(time);
-            return mFlinging;
-        }
-
-        private boolean postFling(long time) {
-            // The idea here is to estimate the time when the pointer would have
-            // traveled one tick distance unit given the current fling velocity.
-            // This effect creates continuity of motion.
-            if (mFlingVelocity >= mConfigMinFlingVelocity) {
-                long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000);
-                postAtTime(mFlingRunnable, time + delay);
-                if (LOCAL_DEBUG) {
-                    Log.d(LOCAL_TAG, "Posted fling: velocity="
-                            + mFlingVelocity + ", delay=" + delay
-                            + ", keyCode=" + mPendingKeyCode);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        private void cancelFling() {
-            if (mFlinging) {
-                removeCallbacks(mFlingRunnable);
-                mFlinging = false;
-            }
-        }
-
-        private final Runnable mFlingRunnable = new Runnable() {
-            @Override
-            public void run() {
-                final long time = SystemClock.uptimeMillis();
-                sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState);
-                mFlingVelocity *= FLING_TICK_DECAY;
-                if (!postFling(time)) {
-                    mFlinging = false;
-                    finishKeys(time);
-                }
-            }
-        };
     }
 
     final class SyntheticKeyboardHandler {
@@ -9272,7 +8991,7 @@
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    void enqueueInputEvent(InputEvent event) {
+    public void enqueueInputEvent(InputEvent event) {
         enqueueInputEvent(event, null, 0, false);
     }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 48686fc..02b3478 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -869,6 +869,42 @@
             "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
 
     /**
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager.Property}
+     * for an app to inform the system that the app can be opted-out from the compatibility
+     * treatment that avoids {@link android.app.Activity#setRequestedOrientation} loops. The loop
+     * can be trigerred by ignoreRequestedOrientation display setting enabled on the device or
+     * by the landscape natural orientation of the device.
+     *
+     * <p>The system could ignore {@link android.app.Activity#setRequestedOrientation}
+     * call from an app if both of the following conditions are true:
+     * <ul>
+     *     <li>Activity has requested orientation more than 2 times within 1-second timer
+     *     <li>Activity is not letterboxed for fixed orientation
+     * </ul>
+     *
+     * <p>Setting this property to {@code false} informs the system that the app must be
+     * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+     * into the treatment.
+     *
+     * <p>Not setting this property at all, or setting this property to {@code true} has no effect.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name=
+     *       "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"
+     *     android:value="false"/&gt;
+     * &lt;/application&gt;
+     * </pre>
+     *
+     * @hide
+     */
+    // TODO(b/274924641): Make this public API.
+    String PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
+            "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
+
+    /**
      * Application level {@link android.content.pm.PackageManager.Property PackageManager
      * .Property} for an app to inform the system that it needs to be opted-out from the
      * compatibility treatment that sandboxes {@link android.view.View} API.
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index ec50c69..aa9225b 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -418,12 +418,15 @@
         });
     }
 
+    @Dispatching(cancellable = false)
     @Override
     public void cancelCancellationSignal(IBinder token) {
         if (mBeamer == null) {
             return;
         }
-        mBeamer.cancel(token);
+        dispatch(() -> {
+            mBeamer.cancel(token);
+        });
     }
 
     @Override
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index d8e64d4..95451a9 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -309,7 +309,7 @@
 
         /** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */
         public void release() {
-            if (mExcludeLayers.length == 0) {
+            if (mExcludeLayers == null || mExcludeLayers.length == 0) {
                 return;
             }
 
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 75e797b..44d517a 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
 import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_IN_WORK;
 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
@@ -212,9 +213,7 @@
         buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom());
 
         ((TextView) findViewById(R.id.open_cross_profile)).setText(
-                getResources().getString(
-                        R.string.miniresolver_open_in_work,
-                        target.loadLabel(packageManagerForTargetUser)));
+                getOpenInWorkMessage(target.loadLabel(packageManagerForTargetUser)));
 
         // The mini-resolver's negative button is reused in this flow to cancel the intent
         ((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel);
@@ -226,6 +225,13 @@
         });
     }
 
+    private String getOpenInWorkMessage(CharSequence targetLabel) {
+        return getSystemService(DevicePolicyManager.class).getResources().getString(
+                MINIRESOLVER_OPEN_IN_WORK,
+                () -> getString(R.string.miniresolver_open_in_work, targetLabel),
+                targetLabel);
+    }
+
     private String getForwardToPersonalMessage() {
         return getSystemService(DevicePolicyManager.class).getResources().getString(
                 FORWARD_INTENT_TO_PERSONAL,
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 73c5207..499d38c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -19,6 +19,10 @@
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
 import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_IN_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_IN_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_USE_PERSONAL_BROWSER;
+import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_USE_WORK_BROWSER;
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
@@ -46,6 +50,7 @@
 import android.app.VoiceInteractor.Prompt;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyResourcesManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -1713,14 +1718,29 @@
             }
         }.execute();
 
-        ((TextView) findViewById(R.id.open_cross_profile)).setText(
-                getResources().getString(
-                        inWorkProfile ? R.string.miniresolver_open_in_personal
-                                : R.string.miniresolver_open_in_work,
-                        otherProfileResolveInfo.getDisplayLabel()));
-        ((Button) findViewById(R.id.use_same_profile_browser)).setText(
-                inWorkProfile ? R.string.miniresolver_use_work_browser
-                        : R.string.miniresolver_use_personal_browser);
+        CharSequence targetDisplayLabel = otherProfileResolveInfo.getDisplayLabel();
+
+        DevicePolicyResourcesManager devicePolicyResourcesManager = getSystemService(
+                DevicePolicyManager.class).getResources();
+
+        if (inWorkProfile) {
+            ((TextView) findViewById(R.id.open_cross_profile)).setText(
+                    devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK,
+                            () -> getString(R.string.miniresolver_open_in_work, targetDisplayLabel),
+                            targetDisplayLabel));
+            ((Button) findViewById(R.id.use_same_profile_browser)).setText(
+                    devicePolicyResourcesManager.getString(MINIRESOLVER_USE_WORK_BROWSER,
+                            () -> getString(R.string.miniresolver_use_work_browser)));
+        } else {
+            ((TextView) findViewById(R.id.open_cross_profile)).setText(
+                    devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL,
+                            () -> getString(R.string.miniresolver_open_in_personal,
+                                    targetDisplayLabel),
+                            targetDisplayLabel));
+            ((Button) findViewById(R.id.use_same_profile_browser)).setText(
+                    devicePolicyResourcesManager.getString(MINIRESOLVER_USE_PERSONAL_BROWSER,
+                            () -> getString(R.string.miniresolver_use_personal_browser)));
+        }
 
         findViewById(R.id.use_same_profile_browser).setOnClickListener(
                 v -> {
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 8e7fe18..098bce1 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -198,6 +198,11 @@
 
     @Override
     public int getMeasuredType() {
+        if (mDrawable == null) {
+            Log.e(TAG, "getMeasuredType() after recycle()!");
+            return MEASURED_NORMAL;
+        }
+
         int measuredHeight = getMeasuredHeight();
         int minImageHeight;
         if (mIsIsolated) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 76bae8de..3ff6351 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4461,7 +4461,8 @@
 
      See android.credentials.CredentialManager
     -->
-    <string name="config_defaultCredentialProviderService" translatable="false"></string>
+    <string-array name="config_defaultCredentialProviderService" translatable="false">
+    </string-array>
 
     <!-- The package name for the system's smartspace service.
      This service returns smartspace results.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9fc2ed1..7b582da 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3754,7 +3754,7 @@
   <java-symbol type="string" name="config_defaultAppPredictionService" />
   <java-symbol type="string" name="config_defaultContentSuggestionsService" />
   <java-symbol type="string" name="config_defaultCredentialManagerHybridService" />
-  <java-symbol type="string" name="config_defaultCredentialProviderService" />
+  <java-symbol type="array" name="config_defaultCredentialProviderService" />
   <java-symbol type="string" name="config_defaultSearchUiService" />
   <java-symbol type="string" name="config_defaultSmartspaceService" />
   <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index a64bb21..e59b259 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -4034,47 +4034,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+        <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
-        <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+        <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+        <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
-        <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+        <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
 
     </style>
 
@@ -4114,47 +4114,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+        <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
-        <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+        <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+        <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
-        <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+        <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
 
     </style>
 
@@ -4186,47 +4186,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+        <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
-        <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+        <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+        <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
-        <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+        <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
 
     </style>
 
@@ -4357,47 +4357,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+        <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
-        <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+        <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+        <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
-        <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+        <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
 
     </style>
 
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java b/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java
new file mode 100644
index 0000000..e20258a
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Class that tests the APIs of DeviceConfigServiceManager.ServiceRegisterer.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DeviceConfigServiceManagerTest {
+
+    private static final String SERVICE_NAME = "device_config_updatable";
+    private DeviceConfigServiceManager.ServiceRegisterer mRegisterer;
+
+    @Before
+    public void setUp() {
+        mRegisterer = new DeviceConfigServiceManager.ServiceRegisterer(SERVICE_NAME);
+    }
+
+    @Test
+    public void testGetOrThrow() throws DeviceConfigServiceManager.ServiceNotFoundException {
+        if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) {
+            assertThat(mRegisterer.getOrThrow()).isNotNull();
+        } else {
+            assertThrows(DeviceConfigServiceManager.ServiceNotFoundException.class,
+                    mRegisterer::getOrThrow);
+        }
+    }
+
+    @Test
+    public void testGet() {
+        assumeTrue(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+        assertThat(mRegisterer.get()).isNotNull();
+    }
+
+    @Test
+    public void testTryGet() {
+        if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) {
+            assertThat(mRegisterer.tryGet()).isNotNull();
+        } else {
+            assertThat(mRegisterer.tryGet()).isNull();
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index d18e98a..248a5fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -18,6 +18,8 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -146,6 +148,13 @@
                 t.setScale(sc, currentScaleX, currentScaleY);
                 t.apply();
             });
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mTransitions.getMainExecutor().execute(
+                            () -> finishCallback.onTransitionFinished(null, null));
+                }
+            });
             animator.start();
             return true;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index e08d40d..efc90b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -91,6 +91,16 @@
     private Drawable mAppIcon;
     private CharSequence mAppName;
 
+    private int mMenuWidth;
+    private int mMarginMenuTop;
+    private int mMarginMenuStart;
+    private int mMarginMenuSpacing;
+    private int mAppInfoPillHeight;
+    private int mWindowingPillHeight;
+    private int mMoreActionsPillHeight;
+    private int mShadowRadius;
+    private int mCornerRadius;
+
     DesktopModeWindowDecoration(
             Context context,
             DisplayController displayController,
@@ -107,6 +117,29 @@
         mSyncQueue = syncQueue;
 
         loadAppInfo();
+        loadHandleMenuDimensions();
+    }
+
+    private void loadHandleMenuDimensions() {
+        final Resources resources = mDecorWindowContext.getResources();
+        mMenuWidth = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_width);
+        mMarginMenuTop = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_margin_top);
+        mMarginMenuStart = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_margin_start);
+        mMarginMenuSpacing = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_pill_spacing_margin);
+        mAppInfoPillHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_app_info_pill_height);
+        mWindowingPillHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_windowing_pill_height);
+        mShadowRadius = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_shadow_radius);
+        mCornerRadius = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_corner_radius);
+        mMoreActionsPillHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
     }
 
     @Override
@@ -155,6 +188,22 @@
                 taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
         final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
 
+        if (mHandleMenuAppInfoPill != null) {
+            updateHandleMenuPillPositions();
+            startT.setPosition(mHandleMenuAppInfoPill.mWindowSurface,
+                    mHandleMenuAppInfoPillPosition.x, mHandleMenuAppInfoPillPosition.y);
+
+            // Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
+            final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
+            if (shouldShowWindowingPill) {
+                startT.setPosition(mHandleMenuWindowingPill.mWindowSurface,
+                        mHandleMenuWindowingPillPosition.x, mHandleMenuWindowingPillPosition.y);
+            }
+
+            startT.setPosition(mHandleMenuMoreActionsPill.mWindowSurface,
+                    mHandleMenuMoreActionsPillPosition.x, mHandleMenuMoreActionsPillPosition.y);
+        }
+
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -271,64 +320,17 @@
      */
     void createHandleMenu() {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        final Resources resources = mDecorWindowContext.getResources();
-        final int captionWidth = mTaskInfo.getConfiguration()
-                .windowConfiguration.getBounds().width();
-        final int menuWidth = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_width);
-        final int shadowRadius = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_shadow_radius);
-        final int cornerRadius = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_corner_radius);
-        final int marginMenuTop = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_margin_top);
-        final int marginMenuStart = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_margin_start);
-        final int marginMenuSpacing = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_pill_spacing_margin);
-        final int appInfoPillHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_app_info_pill_height);
-        final int windowingPillHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_windowing_pill_height);
-        final int moreActionsPillHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
+        updateHandleMenuPillPositions();
 
-        final int menuX, menuY;
-        if (mRelayoutParams.mLayoutResId
-                == R.layout.desktop_mode_app_controls_window_decor) {
-            // Align the handle menu to the left of the caption.
-            menuX = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX + marginMenuStart;
-            menuY = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY + marginMenuTop;
-        } else {
-            // Position the handle menu at the center of the caption.
-            menuX = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
-                    - mResult.mDecorContainerOffsetX;
-            menuY = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY + marginMenuStart;
-        }
-
-        final int appInfoPillY = menuY;
-        createAppInfoPill(t, menuX, appInfoPillY, menuWidth, appInfoPillHeight, shadowRadius,
-                cornerRadius);
+        createAppInfoPill(t);
 
         // Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
         final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
-        final int windowingPillY = appInfoPillY + appInfoPillHeight + marginMenuSpacing;
         if (shouldShowWindowingPill) {
-            createWindowingPill(t, menuX, windowingPillY, menuWidth, windowingPillHeight,
-                    shadowRadius,
-                    cornerRadius);
+            createWindowingPill(t);
         }
 
-        final int moreActionsPillY;
-        if (shouldShowWindowingPill) {
-            // Take into account the windowing pill height and margins.
-            moreActionsPillY = windowingPillY + windowingPillHeight + marginMenuSpacing;
-        } else {
-            // Just start after the end of the app info pill + margins.
-            moreActionsPillY = appInfoPillY + appInfoPillHeight + marginMenuSpacing;
-        }
-        createMoreActionsPill(t, menuX, moreActionsPillY, menuWidth, moreActionsPillHeight,
-                shadowRadius, cornerRadius);
+        createMoreActionsPill(t);
 
         mSyncQueue.runInSync(transaction -> {
             transaction.merge(t);
@@ -337,31 +339,31 @@
         setupHandleMenu(shouldShowWindowingPill);
     }
 
-    private void createAppInfoPill(SurfaceControl.Transaction t, int x, int y, int width,
-            int height, int shadowRadius, int cornerRadius) {
-        mHandleMenuAppInfoPillPosition.set(x, y);
+    private void createAppInfoPill(SurfaceControl.Transaction t) {
+        final int x = (int) mHandleMenuAppInfoPillPosition.x;
+        final int y = (int) mHandleMenuAppInfoPillPosition.y;
         mHandleMenuAppInfoPill = addWindow(
                 R.layout.desktop_mode_window_decor_handle_menu_app_info_pill,
                 "Menu's app info pill",
-                t, x, y, width, height, shadowRadius, cornerRadius);
+                t, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius);
     }
 
-    private void createWindowingPill(SurfaceControl.Transaction t, int x, int y, int width,
-            int height, int shadowRadius, int cornerRadius) {
-        mHandleMenuWindowingPillPosition.set(x, y);
+    private void createWindowingPill(SurfaceControl.Transaction t) {
+        final int x = (int) mHandleMenuWindowingPillPosition.x;
+        final int y = (int) mHandleMenuWindowingPillPosition.y;
         mHandleMenuWindowingPill = addWindow(
                 R.layout.desktop_mode_window_decor_handle_menu_windowing_pill,
                 "Menu's windowing pill",
-                t, x, y, width, height, shadowRadius, cornerRadius);
+                t, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius);
     }
 
-    private void createMoreActionsPill(SurfaceControl.Transaction t, int x, int y, int width,
-            int height, int shadowRadius, int cornerRadius) {
-        mHandleMenuMoreActionsPillPosition.set(x, y);
+    private void createMoreActionsPill(SurfaceControl.Transaction t) {
+        final int x = (int) mHandleMenuMoreActionsPillPosition.x;
+        final int y = (int) mHandleMenuMoreActionsPillPosition.y;
         mHandleMenuMoreActionsPill = addWindow(
                 R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill,
                 "Menu's more actions pill",
-                t, x, y, width, height, shadowRadius, cornerRadius);
+                t, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius);
     }
 
     private void setupHandleMenu(boolean windowingPillShown) {
@@ -413,6 +415,45 @@
     }
 
     /**
+     * Updates the handle menu pills' position variables to reflect their next positions
+     */
+    private void updateHandleMenuPillPositions() {
+        final int menuX, menuY;
+        final int captionWidth = mTaskInfo.getConfiguration()
+                .windowConfiguration.getBounds().width();
+        if (mRelayoutParams.mLayoutResId
+                == R.layout.desktop_mode_app_controls_window_decor) {
+            // Align the handle menu to the left of the caption.
+            menuX = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX + mMarginMenuStart;
+            menuY = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY + mMarginMenuTop;
+        } else {
+            // Position the handle menu at the center of the caption.
+            menuX = mRelayoutParams.mCaptionX + (captionWidth / 2) - (mMenuWidth / 2)
+                    - mResult.mDecorContainerOffsetX;
+            menuY = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY + mMarginMenuStart;
+        }
+
+        // App Info pill setup.
+        final int appInfoPillY = menuY;
+        mHandleMenuAppInfoPillPosition.set(menuX, appInfoPillY);
+
+        // Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
+        final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
+
+        final int windowingPillY, moreActionsPillY;
+        if (shouldShowWindowingPill) {
+            windowingPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
+            mHandleMenuWindowingPillPosition.set(menuX, windowingPillY);
+            moreActionsPillY = windowingPillY + mWindowingPillHeight + mMarginMenuSpacing;
+            mHandleMenuMoreActionsPillPosition.set(menuX, moreActionsPillY);
+        } else {
+            // Just start after the end of the app info pill + margins.
+            moreActionsPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
+            mHandleMenuMoreActionsPillPosition.set(menuX, moreActionsPillY);
+        }
+    }
+
+    /**
      * Close the handle menu window
      */
     void closeHandleMenu() {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 5d79104..70c36a5 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -55,6 +55,10 @@
         // GCC false-positives on this warning, and since we -Werror that's
         // a problem
         "-Wno-free-nonheap-object",
+
+        // Do not de-optimise cold code paths in AFDO.
+        // Some code paths might be infrequently executed but critical to latency.
+        "-fno-profile-sample-accurate",
     ],
 
     include_dirs: [
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 8ea71f1..1f92968 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -28,6 +28,7 @@
 #include <SkMultiPictureDocument.h>
 #include <SkOverdrawCanvas.h>
 #include <SkOverdrawColorFilter.h>
+#include <SkPaintFilterCanvas.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
 #include <SkRect.h>
@@ -36,15 +37,15 @@
 #include <SkStream.h>
 #include <SkString.h>
 #include <SkTypeface.h>
-#include "include/gpu/GpuTypes.h" // from Skia
 #include <android-base/properties.h>
+#include <gui/TraceUtils.h>
 #include <unistd.h>
 
 #include <sstream>
 
-#include <gui/TraceUtils.h>
 #include "LightingInfo.h"
 #include "VectorDrawable.h"
+#include "include/gpu/GpuTypes.h"  // from Skia
 #include "thread/CommonPool.h"
 #include "tools/SkSharingProc.h"
 #include "utils/Color.h"
@@ -449,6 +450,23 @@
     }
 }
 
+class ForceDitherCanvas : public SkPaintFilterCanvas {
+public:
+    ForceDitherCanvas(SkCanvas* canvas) : SkPaintFilterCanvas(canvas) {}
+
+protected:
+    bool onFilter(SkPaint& paint) const override {
+        paint.setDither(true);
+        return true;
+    }
+
+    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+        // We unroll the drawable using "this" canvas, so that draw calls contained inside will
+        // get dithering applied
+        drawable->draw(this, matrix);
+    }
+};
+
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                                const std::vector<sp<RenderNode>>& nodes, bool opaque,
                                const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
@@ -503,6 +521,12 @@
         canvas->clear(SK_ColorTRANSPARENT);
     }
 
+    std::optional<ForceDitherCanvas> forceDitherCanvas;
+    if (shouldForceDither()) {
+        forceDitherCanvas.emplace(canvas);
+        canvas = &forceDitherCanvas.value();
+    }
+
     if (1 == nodes.size()) {
         if (!nodes[0]->nothingToDraw()) {
             RenderNodeDrawable root(nodes[0].get(), canvas);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index befee89..0763b06 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -98,6 +98,8 @@
 
     bool isCapturingSkp() const { return mCaptureMode != CaptureMode::None; }
 
+    virtual bool shouldForceDither() const { return mColorMode != ColorMode::Default; }
+
 private:
     void renderFrameImpl(const SkRect& clip,
                          const std::vector<sp<RenderNode>>& nodes, bool opaque,
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index c8f2e69..6f1b99b 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -203,6 +203,11 @@
     return nullptr;
 }
 
+bool SkiaVulkanPipeline::shouldForceDither() const {
+    if (mVkSurface && mVkSurface->isBeyond8Bit()) return false;
+    return SkiaPipeline::shouldForceDither();
+}
+
 void SkiaVulkanPipeline::onContextDestroyed() {
     if (mVkSurface) {
         vulkanManager().destroySurface(mVkSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index d921ddb..0713e93 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -63,6 +63,8 @@
 protected:
     void onContextDestroyed() override;
 
+    bool shouldForceDither() const override;
+
 private:
     renderthread::VulkanManager& vulkanManager();
     renderthread::VulkanSurface* mVkSurface = nullptr;
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 21b6c44..ae4f057 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -530,6 +530,16 @@
     }
 }
 
+bool VulkanSurface::isBeyond8Bit() const {
+    switch (mWindowInfo.bufferFormat) {
+        case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+            return true;
+        default:
+            return false;
+    }
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index e2ddc6b..3b69b73 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -48,6 +48,8 @@
 
     void setColorSpace(sk_sp<SkColorSpace> colorSpace);
 
+    bool isBeyond8Bit() const;
+
 private:
     /*
      * All structs/methods in this private section are specifically for use by the VulkanManager
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b4fbc97..b1d2e33 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -661,7 +661,10 @@
      */
     public static final int ENCODED_SURROUND_OUTPUT_MANUAL = 3;
 
-    /** @hide */
+    /**
+     * @hide
+     * This list contains all the flags that can be used in internal APIs for volume
+     * related operations */
     @IntDef(flag = true, prefix = "FLAG", value = {
             FLAG_SHOW_UI,
             FLAG_ALLOW_RINGER_MODES,
@@ -681,16 +684,64 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {}
 
-    /** @hide */
+    /**
+     * @hide
+     * This list contains all the flags that can be used in SDK-visible methods for volume
+     * related operations.
+     * See for instance {@link #adjustVolume(int, int)},
+     * {@link #adjustStreamVolume(int, int, int)},
+     * {@link #adjustSuggestedStreamVolume(int, int, int)},
+     * {@link #adjustVolumeGroupVolume(int, int, int)},
+     * {@link #setStreamVolume(int, int, int)}
+     * The list contains all volume flags, but the values commented out of the list are there for
+     * maintenance reasons (for when adding flags or changing their visibility),
+     * and to document why some are not in the list (hidden or SystemApi). */
     @IntDef(flag = true, prefix = "FLAG", value = {
             FLAG_SHOW_UI,
             FLAG_ALLOW_RINGER_MODES,
             FLAG_PLAY_SOUND,
             FLAG_REMOVE_SOUND_AND_VIBRATE,
             FLAG_VIBRATE,
+            //FLAG_FIXED_VOLUME,             removed due to @hide
+            //FLAG_BLUETOOTH_ABS_VOLUME,     removed due to @SystemApi
+            //FLAG_SHOW_SILENT_HINT,         removed due to @hide
+            //FLAG_HDMI_SYSTEM_AUDIO_VOLUME, removed due to @hide
+            //FLAG_ACTIVE_MEDIA_ONLY,        removed due to @hide
+            //FLAG_SHOW_UI_WARNINGS,         removed due to @hide
+            //FLAG_SHOW_VIBRATE_HINT,        removed due to @hide
+            //FLAG_FROM_KEY,                 removed due to @SystemApi
+            //FLAG_ABSOLUTE_VOLUME,          removed due to @hide
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PublicVolumeFlags {}
+
+    /**
+     * @hide
+     * Like PublicVolumeFlags, but for all the flags that can be used in @SystemApi methods for
+     * volume related operations.
+     * See for instance {@link #setVolumeIndexForAttributes(AudioAttributes, int, int)},
+     * {@link #setVolumeGroupVolumeIndex(int, int, int)},
+     * {@link #setStreamVolumeForUid(int, int, int, String, int, int, int)},
+     * {@link #adjustStreamVolumeForUid(int, int, int, String, int, int, int)},
+     * {@link #adjustSuggestedStreamVolumeForUid(int, int, int, String, int, int, int)}
+     * The list contains all volume flags, but the values commented out of the list are there for
+     * maintenance reasons (for when adding flags or changing their visibility),
+     * and to document which hidden values are not in the list. */
+    @IntDef(flag = true, prefix = "FLAG", value = {
+            FLAG_SHOW_UI,
+            FLAG_ALLOW_RINGER_MODES,
+            FLAG_PLAY_SOUND,
+            FLAG_REMOVE_SOUND_AND_VIBRATE,
+            FLAG_VIBRATE,
+            //FLAG_FIXED_VOLUME,             removed due to @hide
             FLAG_BLUETOOTH_ABS_VOLUME,
-            FLAG_HDMI_SYSTEM_AUDIO_VOLUME,
+            //FLAG_SHOW_SILENT_HINT,         removed due to @hide
+            //FLAG_HDMI_SYSTEM_AUDIO_VOLUME, removed due to @hide
+            //FLAG_ACTIVE_MEDIA_ONLY,        removed due to @hide
+            //FLAG_SHOW_UI_WARNINGS,         removed due to @hide
+            //FLAG_SHOW_VIBRATE_HINT,        removed due to @hide
             FLAG_FROM_KEY,
+            //FLAG_ABSOLUTE_VOLUME,          removed due to @hide
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SystemVolumeFlags {}
@@ -981,13 +1032,13 @@
      * @param direction The direction to adjust the volume. One of
      *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
      *            {@link #ADJUST_SAME}.
-     * @param flags One or more flags.
+     * @param flags
      * @see #adjustVolume(int, int)
      * @see #setStreamVolume(int, int, int)
      * @throws SecurityException if the adjustment triggers a Do Not Disturb change
      *   and the caller is not granted notification policy access.
      */
-    public void adjustStreamVolume(int streamType, int direction, int flags) {
+    public void adjustStreamVolume(int streamType, int direction, @PublicVolumeFlags int flags) {
         final IAudioService service = getService();
         try {
             service.adjustStreamVolumeWithAttribution(streamType, direction, flags,
@@ -1014,13 +1065,13 @@
      *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
      *            {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
      *            {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
-     * @param flags One or more flags.
+     * @param flags
      * @see #adjustSuggestedStreamVolume(int, int, int)
      * @see #adjustStreamVolume(int, int, int)
      * @see #setStreamVolume(int, int, int)
      * @see #isVolumeFixed()
      */
-    public void adjustVolume(int direction, int flags) {
+    public void adjustVolume(int direction, @PublicVolumeFlags int flags) {
         MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
         helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
     }
@@ -1043,13 +1094,14 @@
      * @param suggestedStreamType The stream type that will be used if there
      *            isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
      *            valid here.
-     * @param flags One or more flags.
+     * @param flags
      * @see #adjustVolume(int, int)
      * @see #adjustStreamVolume(int, int, int)
      * @see #setStreamVolume(int, int, int)
      * @see #isVolumeFixed()
      */
-    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
+    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType,
+            @PublicVolumeFlags int flags) {
         MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
         helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
     }
@@ -1334,14 +1386,14 @@
      * @param streamType The stream whose volume index should be set.
      * @param index The volume index to set. See
      *            {@link #getStreamMaxVolume(int)} for the largest valid value.
-     * @param flags One or more flags.
+     * @param flags
      * @see #getStreamMaxVolume(int)
      * @see #getStreamVolume(int)
      * @see #isVolumeFixed()
      * @throws SecurityException if the volume change triggers a Do Not Disturb change
      *   and the caller is not granted notification policy access.
      */
-    public void setStreamVolume(int streamType, int index, int flags) {
+    public void setStreamVolume(int streamType, int index, @PublicVolumeFlags int flags) {
         final IAudioService service = getService();
         try {
             service.setStreamVolumeWithAttribution(streamType, index, flags,
@@ -1357,7 +1409,7 @@
      * @param index The volume index to set. See
      *          {@link #getMaxVolumeIndexForAttributes(AudioAttributes)} for the largest valid value
      *          {@link #getMinVolumeIndexForAttributes(AudioAttributes)} for the lowest valid value.
-     * @param flags One or more flags.
+     * @param flags
      * @see #getMaxVolumeIndexForAttributes(AudioAttributes)
      * @see #getMinVolumeIndexForAttributes(AudioAttributes)
      * @see #isVolumeFixed()
@@ -1365,7 +1417,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) {
+    public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index,
+            @SystemVolumeFlags int flags) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
         int groupId = getVolumeGroupIdForAttributes(attr);
@@ -1451,7 +1504,7 @@
      * @param index The volume index to set. See
      *          {@link #getVolumeGroupMaxVolumeIndex(id)} for the largest valid value
      *          {@link #getVolumeGroupMinVolumeIndex(id)} for the lowest valid value.
-     * @param flags One or more flags.
+     * @param flags
      * @hide
      */
     @SystemApi
@@ -1552,11 +1605,11 @@
      * @param direction The direction to adjust the volume. One of
      *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
      *            {@link #ADJUST_SAME}.
-     * @param flags One or more flags.
+     * @param flags
      * @throws SecurityException if the adjustment triggers a Do Not Disturb change and the caller
      * is not granted notification policy access.
      */
-    public void adjustVolumeGroupVolume(int groupId, int direction, @SystemVolumeFlags int flags) {
+    public void adjustVolumeGroupVolume(int groupId, int direction, @PublicVolumeFlags int flags) {
         IAudioService service = getService();
         try {
             service.adjustVolumeGroupVolume(groupId, direction, flags,
@@ -8234,7 +8287,7 @@
      *         {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
      *         {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
      *         {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
-     * @param flags One or more flags.
+     * @param flags
      * @param packageName the package name of client application
      * @param uid the uid of client application
      * @param pid the pid of client application
@@ -8247,7 +8300,8 @@
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction, int flags,
+    public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction,
+            @SystemVolumeFlags int flags,
             @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
         try {
             getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
@@ -8277,7 +8331,7 @@
      * @param direction The direction to adjust the volume. One of
      *         {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
      *         {@link #ADJUST_SAME}.
-     * @param flags One or more flags.
+     * @param flags
      * @param packageName the package name of client application
      * @param uid the uid of client application
      * @param pid the pid of client application
@@ -8290,7 +8344,8 @@
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+    public void adjustStreamVolumeForUid(int streamType, int direction,
+            @SystemVolumeFlags int flags,
             @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
         try {
             getService().adjustStreamVolumeForUid(streamType, direction, flags, packageName, uid,
@@ -8314,7 +8369,7 @@
      * @param streamType The stream whose volume index should be set.
      * @param index The volume index to set. See
      *         {@link #getStreamMaxVolume(int)} for the largest valid value.
-     * @param flags One or more flags.
+     * @param flags
      * @param packageName the package name of client application
      * @param uid the uid of client application
      * @param pid the pid of client application
@@ -8328,7 +8383,8 @@
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void setStreamVolumeForUid(int streamType, int index, int flags,
+    public void setStreamVolumeForUid(int streamType, int index,
+            @SystemVolumeFlags int flags,
             @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
         try {
             getService().setStreamVolumeForUid(streamType, index, flags, packageName, uid, pid,
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index f9d4efe..fe5afc5 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -268,7 +268,7 @@
     boolean isVolumeControlUsingVolumeGroups();
 
     @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
-    oneway void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register);
+    void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register);
 
     @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
     void setNotifAliasRingForTest(boolean alias);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 031c3ff..0d9bd65 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -1169,7 +1169,7 @@
          * @param flags flags containing extra action or information regarding the volume change
          */
         void onVolumeChanged(@NonNull MediaSession.Token sessionToken,
-                @AudioManager.Flags int flags);
+                @AudioManager.SystemVolumeFlags int flags);
 
         /**
          * Called when the default remote session is changed where the default remote session
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 9a4aa33..dea7f03 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -43,6 +43,8 @@
 
 #include <android_runtime/android_hardware_HardwareBuffer.h>
 
+#include <android-base/stringprintf.h>
+
 #include <binder/MemoryDealer.h>
 
 #include <cutils/compiler.h>
@@ -1276,7 +1278,8 @@
                     ALOGE("Could not create MediaCodec.BufferInfo.");
                     env->ExceptionClear();
                 }
-                jniThrowException(env, "java/lang/IllegalStateException", NULL);
+                jniThrowException(env, "java/lang/IllegalStateException",
+                                  "Fatal error: could not create MediaCodec.BufferInfo object");
                 return;
             }
 
@@ -1309,7 +1312,8 @@
                     ALOGE("Could not create CodecException object.");
                     env->ExceptionClear();
                 }
-                jniThrowException(env, "java/lang/IllegalStateException", NULL);
+                jniThrowException(env, "java/lang/IllegalStateException",
+                                  "Fatal error: could not create CodecException object");
                 return;
             }
 
@@ -1322,7 +1326,9 @@
             CHECK(msg->findMessage("format", &format));
 
             if (OK != ConvertMessageToMap(env, format, &obj)) {
-                jniThrowException(env, "java/lang/IllegalStateException", NULL);
+                jniThrowException(env, "java/lang/IllegalStateException",
+                                  "Fatal error: failed to convert format "
+                                  "from native to Java object");
                 return;
             }
 
@@ -1353,7 +1359,8 @@
 
     status_t err = ConvertMessageToMap(env, data, &obj);
     if (err != OK) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Fatal error: failed to convert format from native to Java object");
         return;
     }
 
@@ -1374,7 +1381,8 @@
 
     status_t err = ConvertMessageToMap(env, data, &obj);
     if (err != OK) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Fatal error: failed to convert format from native to Java object");
         return;
     }
 
@@ -1385,6 +1393,18 @@
     env->DeleteLocalRef(obj);
 }
 
+std::string JMediaCodec::getExceptionMessage(const char *msg = nullptr) const {
+    if (mCodec == nullptr) {
+        return msg ?: "";
+    }
+    std::string prefix = "";
+    if (msg && msg[0] != '\0') {
+        prefix.append(msg);
+        prefix.append("\n");
+    }
+    return prefix + mCodec->getErrorLog().extract();
+}
+
 void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatCallbackNotify:
@@ -1471,9 +1491,17 @@
     env->Throw(exception);
 }
 
+static std::string GetExceptionMessage(const sp<JMediaCodec> &codec, const char *msg) {
+    if (codec == NULL) {
+        return msg ?: "codec is released already";
+    }
+    return codec->getExceptionMessage(msg);
+}
+
 static jint throwExceptionAsNecessary(
         JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL,
-        const char *msg = NULL, const sp<ICrypto>& crypto = NULL) {
+        const char *msg = NULL, const sp<ICrypto>& crypto = NULL,
+        const sp<JMediaCodec> &codec = NULL) {
     switch (err) {
         case OK:
             return 0;
@@ -1488,23 +1516,38 @@
             return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
 
         case INVALID_OPERATION:
-            jniThrowException(env, "java/lang/IllegalStateException", msg);
+            jniThrowException(
+                    env, "java/lang/IllegalStateException",
+                    GetExceptionMessage(codec, msg).c_str());
             return 0;
 
         case BAD_VALUE:
-            jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+            jniThrowException(
+                    env, "java/lang/IllegalArgumentException",
+                    GetExceptionMessage(codec, msg).c_str());
             return 0;
 
         default:
             if (isCryptoError(err)) {
-                throwCryptoException(env, err, msg, crypto);
+                throwCryptoException(
+                        env, err,
+                        GetExceptionMessage(codec, msg).c_str(),
+                        crypto);
                 return 0;
             }
-            throwCodecException(env, err, actionCode, msg);
+            throwCodecException(
+                    env, err, actionCode,
+                    GetExceptionMessage(codec, msg).c_str());
             return 0;
     }
 }
 
+static jint throwExceptionAsNecessary(
+        JNIEnv *env, status_t err, const sp<JMediaCodec> &codec,
+        int32_t actionCode = ACTION_CODE_FATAL) {
+    return throwExceptionAsNecessary(env, err, actionCode, NULL, NULL, codec);
+}
+
 static void android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListener(
         JNIEnv *env,
         jobject thiz,
@@ -1512,13 +1555,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->enableOnFirstTunnelFrameReadyListener(enabled);
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_native_enableOnFrameRenderedListener(
@@ -1528,13 +1571,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->enableOnFrameRenderedListener(enabled);
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_native_setCallback(
@@ -1544,13 +1587,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->setCallback(cb);
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_native_configure(
@@ -1564,7 +1607,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -1602,7 +1645,7 @@
 
     err = codec->configure(format, bufferProducer, crypto, descrambler, flags);
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_native_setSurface(
@@ -1612,7 +1655,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -1631,7 +1674,7 @@
     }
 
     status_t err = codec->setSurface(bufferProducer);
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
@@ -1735,7 +1778,7 @@
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -1749,7 +1792,7 @@
     }
     status_t err = codec->setInputSurface(persistentSurface);
     if (err != NO_ERROR) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(env, err, codec);
     }
 }
 
@@ -1759,7 +1802,7 @@
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -1767,7 +1810,7 @@
     sp<IGraphicBufferProducer> bufferProducer;
     status_t err = codec->createInputSurface(&bufferProducer);
     if (err != NO_ERROR) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(env, err, codec);
         return NULL;
     }
 
@@ -1782,13 +1825,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->start();
 
-    throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed");
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
@@ -1797,13 +1840,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->stop();
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
@@ -1812,7 +1855,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -1825,7 +1868,7 @@
         // trigger an IllegalStateException.
         err = UNKNOWN_ERROR;
     }
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
@@ -1834,13 +1877,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->flush();
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_queueInputBuffer(
@@ -1856,7 +1899,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -1866,7 +1909,8 @@
             index, offset, size, timestampUs, flags, &errorDetailMsg);
 
     throwExceptionAsNecessary(
-            env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
+            env, err, ACTION_CODE_FATAL,
+            codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
 }
 
 struct NativeCryptoInfo {
@@ -1890,7 +1934,9 @@
         } else if (jmode == gCryptoModes.AesCbc) {
             mMode = CryptoPlugin::kMode_AES_CBC;
         }  else {
-            throwExceptionAsNecessary(env, INVALID_OPERATION);
+            throwExceptionAsNecessary(
+                    env, INVALID_OPERATION, ACTION_CODE_FATAL,
+                    base::StringPrintf("unrecognized crypto mode: %d", jmode).c_str());
             return;
         }
 
@@ -2026,7 +2072,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -2056,7 +2102,9 @@
     } else if (jmode == gCryptoModes.AesCbc) {
         mode = CryptoPlugin::kMode_AES_CBC;
     }  else {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(
+                env, INVALID_OPERATION, ACTION_CODE_FATAL,
+                base::StringPrintf("Unrecognized crypto mode: %d", jmode).c_str());
         return;
     }
 
@@ -2175,8 +2223,8 @@
     subSamples = NULL;
 
     throwExceptionAsNecessary(
-            env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str(),
-            codec->getCrypto());
+            env, err, ACTION_CODE_FATAL,
+            codec->getExceptionMessage(errorDetailMsg.c_str()).c_str(), codec->getCrypto());
 }
 
 static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) {
@@ -2518,14 +2566,16 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == nullptr || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     sp<AMessage> tunings;
     status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
     if (err != OK) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(
+                env, err, ACTION_CODE_FATAL,
+                "error occurred while converting tunings from Java to native");
         return;
     }
 
@@ -2545,15 +2595,23 @@
         }
         env->MonitorExit(lock.get());
     } else {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(
+                env, INVALID_OPERATION, ACTION_CODE_FATAL,
+                "Failed to grab lock for a LinearBlock object");
         return;
     }
 
     AString errorDetailMsg;
     if (codec->hasCryptoOrDescrambler()) {
         if (!memory) {
+            // It means there was an unexpected failure in extractMemoryFromContext above
             ALOGI("queueLinearBlock: no ashmem memory for encrypted content");
-            throwExceptionAsNecessary(env, BAD_VALUE);
+            throwExceptionAsNecessary(
+                    env, BAD_VALUE, ACTION_CODE_FATAL,
+                    "Unexpected error: the input buffer is not compatible with "
+                    "the secure codec, and a fallback logic failed.\n"
+                    "Suggestion: please try including the secure codec when calling "
+                    "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
             return;
         }
         auto cryptoInfo =
@@ -2577,14 +2635,22 @@
         ALOGI_IF(err != OK, "queueEncryptedLinearBlock returned err = %d", err);
     } else {
         if (!buffer) {
+            // It means there was an unexpected failure in extractBufferFromContext above
             ALOGI("queueLinearBlock: no C2Buffer found");
-            throwExceptionAsNecessary(env, BAD_VALUE);
+            throwExceptionAsNecessary(
+                    env, BAD_VALUE, ACTION_CODE_FATAL,
+                    "Unexpected error: the input buffer is not compatible with "
+                    "the non-secure codec, and a fallback logic failed.\n"
+                    "Suggestion: please do not include the secure codec when calling "
+                    "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
             return;
         }
         err = codec->queueBuffer(
                 index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
     }
-    throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+    throwExceptionAsNecessary(
+            env, err, ACTION_CODE_FATAL,
+            codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
 }
 
 static void android_media_MediaCodec_native_queueHardwareBuffer(
@@ -2595,14 +2661,16 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     sp<AMessage> tunings;
     status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
     if (err != OK) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(
+                env, err, ACTION_CODE_FATAL,
+                "error occurred while converting tunings from Java to native");
         return;
     }
 
@@ -2627,7 +2695,9 @@
         ALOGW("Failed to wrap AHardwareBuffer into C2GraphicAllocation");
         native_handle_close(handle);
         native_handle_delete(handle);
-        throwExceptionAsNecessary(env, BAD_VALUE);
+        throwExceptionAsNecessary(
+                env, BAD_VALUE, ACTION_CODE_FATAL,
+                "HardwareBuffer not recognized");
         return;
     }
     std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -2636,7 +2706,9 @@
     AString errorDetailMsg;
     err = codec->queueBuffer(
             index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
-    throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+    throwExceptionAsNecessary(
+            env, err, ACTION_CODE_FATAL,
+            codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
 }
 
 static void android_media_MediaCodec_native_getOutputFrame(
@@ -2646,13 +2718,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->getOutputFrame(env, frame, index);
     if (err != OK) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(env, err, codec);
     }
 }
 
@@ -2663,7 +2735,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return -1;
     }
 
@@ -2674,7 +2746,7 @@
         return (jint) index;
     }
 
-    return throwExceptionAsNecessary(env, err);
+    return throwExceptionAsNecessary(env, err, codec);
 }
 
 static jint android_media_MediaCodec_dequeueOutputBuffer(
@@ -2684,7 +2756,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return 0;
     }
 
@@ -2696,7 +2768,7 @@
         return (jint) index;
     }
 
-    return throwExceptionAsNecessary(env, err);
+    return throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_releaseOutputBuffer(
@@ -2707,13 +2779,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
@@ -2722,13 +2794,13 @@
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t err = codec->signalEndOfInputStream();
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static jobject android_media_MediaCodec_getFormatNative(
@@ -2738,7 +2810,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2749,7 +2821,7 @@
         return format;
     }
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 
     return NULL;
 }
@@ -2761,7 +2833,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2772,7 +2844,7 @@
         return format;
     }
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 
     return NULL;
 }
@@ -2784,7 +2856,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2797,7 +2869,7 @@
 
     // if we're out of memory, an exception was already thrown
     if (err != NO_MEMORY) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(env, err, codec);
     }
 
     return NULL;
@@ -2810,7 +2882,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2823,7 +2895,7 @@
 
     // if we're out of memory, an exception was already thrown
     if (err != NO_MEMORY) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(env, err, codec);
     }
 
     return NULL;
@@ -2836,7 +2908,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2849,7 +2921,7 @@
 
     // if we're out of memory, an exception was already thrown
     if (err != NO_MEMORY) {
-        throwExceptionAsNecessary(env, err);
+        throwExceptionAsNecessary(env, err, codec);
     }
 
     return NULL;
@@ -2862,7 +2934,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2873,7 +2945,7 @@
         return name;
     }
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 
     return NULL;
 }
@@ -2885,7 +2957,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -2896,7 +2968,7 @@
         return codecInfoObj;
     }
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 
     return NULL;
 }
@@ -2908,7 +2980,8 @@
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
     if (codec == NULL || codec->initCheck() != OK) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          GetExceptionMessage(codec, NULL).c_str());
         return 0;
     }
 
@@ -2937,7 +3010,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -2948,7 +3021,7 @@
         err = codec->setParameters(params);
     }
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, codec);
 }
 
 static void android_media_MediaCodec_setVideoScalingMode(
@@ -2956,13 +3029,14 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
             && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          String8::format("Unrecognized mode: %d", mode));
         return;
     }
 
@@ -2974,7 +3048,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
@@ -2986,14 +3060,14 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
     jobject ret = NULL;
     status_t status = codec->querySupportedVendorParameters(env, &ret);
     if (status != OK) {
-        throwExceptionAsNecessary(env, status);
+        throwExceptionAsNecessary(env, status, codec);
     }
 
     return ret;
@@ -3004,7 +3078,7 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return NULL;
     }
 
@@ -3021,13 +3095,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t status = codec->subscribeToVendorParameters(env, names);
     if (status != OK) {
-        throwExceptionAsNecessary(env, status);
+        throwExceptionAsNecessary(env, status, codec);
     }
     return;
 }
@@ -3037,13 +3111,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL || codec->initCheck() != OK) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
         return;
     }
 
     status_t status = codec->unsubscribeFromVendorParameters(env, names);
     if (status != OK) {
-        throwExceptionAsNecessary(env, status);
+        throwExceptionAsNecessary(env, status, codec);
     }
     return;
 }
@@ -3440,11 +3514,15 @@
         if (!context->mReadonlyMapping) {
             const C2BufferData data = buffer->data();
             if (data.type() != C2BufferData::LINEAR) {
-                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                throwExceptionAsNecessary(
+                        env, INVALID_OPERATION, ACTION_CODE_FATAL,
+                        "Underlying buffer is not a linear buffer");
                 return nullptr;
             }
             if (data.linearBlocks().size() != 1u) {
-                throwExceptionAsNecessary(env, INVALID_OPERATION);
+                throwExceptionAsNecessary(
+                        env, INVALID_OPERATION, ACTION_CODE_FATAL,
+                        "Underlying buffer contains more than one block");
                 return nullptr;
             }
             C2ConstLinearBlock block = data.linearBlocks().front();
@@ -3492,7 +3570,9 @@
                 false,  // readOnly
                 true /* clearBuffer */);
     }
-    throwExceptionAsNecessary(env, INVALID_OPERATION);
+    throwExceptionAsNecessary(
+            env, INVALID_OPERATION, ACTION_CODE_FATAL,
+            "Underlying buffer is empty");
     return nullptr;
 }
 
@@ -3515,7 +3595,9 @@
         }
         const char *cstr = env->GetStringUTFChars(jstr, nullptr);
         if (cstr == nullptr) {
-            throwExceptionAsNecessary(env, BAD_VALUE);
+            throwExceptionAsNecessary(
+                    env, BAD_VALUE, ACTION_CODE_FATAL,
+                    "Error converting Java string to native");
             return;
         }
         names->emplace_back(cstr);
@@ -3567,6 +3649,7 @@
     }
     status_t err = MediaCodec::CanFetchLinearBlock(names, &isCompatible);
     if (err != OK) {
+        // TODO: CodecErrorLog
         throwExceptionAsNecessary(env, err);
     }
     return isCompatible;
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 616c31b..fbaf64f 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -176,6 +176,8 @@
 
     const sp<ICrypto> &getCrypto() { return mCrypto; }
 
+    std::string getExceptionMessage(const char *msg) const;
+
 protected:
     virtual ~JMediaCodec();
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 6f5015d..24f92c0 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -5,7 +5,7 @@
  * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0N
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -74,7 +74,6 @@
     override fun onNewIntent(intent: Intent) {
         super.onNewIntent(intent)
         setIntent(intent)
-        Log.d(Constants.LOG_TAG, "Existing activity received new intent")
         try {
             val viewModel: CredentialSelectorViewModel by viewModels()
             val (isCancellationRequest, shouldShowCancellationUi, appDisplayName) =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7a97b78..b0a1927 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3748,7 +3748,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 216;
+            private static final int SETTINGS_VERSION = 217;
 
             private final int mUserId;
 
@@ -5711,7 +5711,7 @@
                             .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
                     if (currentSetting.isNull()) {
                         final int resourceId =
-                            com.android.internal.R.string.config_defaultCredentialProviderService;
+                            com.android.internal.R.array.config_defaultCredentialProviderService;
                         final Resources resources = getContext().getResources();
                         // If the config has not be defined we might get an exception. We also get
                         // values from both the string array type and the single string in case the
@@ -5771,6 +5771,39 @@
                     currentVersion = 216;
                 }
 
+                if (currentVersion == 216) {
+                    // Version 216: Set a default value for Credential Manager service.
+                    // We are doing this migration again because of an incorrect setting.
+
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting currentSetting = secureSettings
+                            .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
+                    if (currentSetting.isNull()) {
+                        final int resourceId =
+                            com.android.internal.R.array.config_defaultCredentialProviderService;
+                        final Resources resources = getContext().getResources();
+                        // If the config has not be defined we might get an exception.
+                        final List<String> providers = new ArrayList<>();
+                        try {
+                            providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
+                        } catch (Resources.NotFoundException e) {
+                            Slog.w(LOG_TAG,
+                                "Get default array Cred Provider not found: " + e.toString());
+                        }
+
+                        if (!providers.isEmpty()) {
+                            final String defaultValue = String.join(":", providers);
+                            Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as CredMan Service "
+                                    + "for user " + userId);
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
+                                    Settings.Secure.CREDENTIAL_SERVICE, defaultValue, null, true,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+
+                    currentVersion = 217;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
index df27f6a..bd99a8b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
@@ -170,6 +170,7 @@
                 "widget",
                 "wifi",
                 "window_manager",
-                "window_manager_native_boot"
+                "window_manager_native_boot",
+                "wrong"
             ));
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ff57052..36a0b5d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -872,6 +872,20 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name=".contrast.ContrastDialogActivity"
+            android:label="@string/quick_settings_contrast_label"
+            android:theme="@style/Theme.SystemUI.ContrastDialog"
+            android:finishOnCloseSystemDialogs="true"
+            android:launchMode="singleInstance"
+            android:excludeFromRecents="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.intent.action.SHOW_CONTRAST_DIALOG" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".ForegroundServicesDialog"
             android:process=":fgservices"
             android:excludeFromRecents="true"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 4df7a44..3ec3b5c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -25,8 +25,10 @@
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.ClockAnimations
+import com.android.systemui.plugins.ClockConfig
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.plugins.ClockEvents
+import com.android.systemui.plugins.ClockFaceConfig
 import com.android.systemui.plugins.ClockFaceController
 import com.android.systemui.plugins.ClockFaceEvents
 import com.android.systemui.plugins.ClockSettings
@@ -63,6 +65,8 @@
     override lateinit var animations: DefaultClockAnimations
         private set
 
+    override val config = ClockConfig(hasCustomPositionUpdatedAnimation = true)
+
     init {
         val parent = FrameLayout(ctx)
         smallClock =
@@ -103,6 +107,8 @@
         private var isRegionDark = false
         protected var targetRegion: Rect? = null
 
+        override val config = ClockFaceConfig()
+
         override var logBuffer: LogBuffer?
             get() = view.logBuffer
             set(value) {
@@ -254,9 +260,6 @@
         override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {
             largeClock.moveForSplitShade(fromRect, toRect, fraction)
         }
-
-        override val hasCustomPositionUpdatedAnimation: Boolean
-            get() = true
     }
 
     class AnimationState(
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index c279053..322fc77 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -63,6 +63,9 @@
     /** A large version of the clock, appropriate when a bigger viewport is available */
     val largeClock: ClockFaceController
 
+    /** Determines the way the hosting app should behave when rendering either clock face */
+    val config: ClockConfig
+
     /** Events that clocks may need to respond to */
     val events: ClockEvents
 
@@ -91,6 +94,9 @@
     /** View that renders the clock face */
     val view: View
 
+    /** Determines the way the hosting app should behave when rendering this clock face */
+    val config: ClockFaceConfig
+
     /** Events specific to this clock face */
     val events: ClockFaceEvents
 
@@ -109,9 +115,6 @@
     /** Call whenever the locale changes */
     fun onLocaleChanged(locale: Locale) {}
 
-    val isReactiveToTone
-        get() = true
-
     /** Call whenever the color palette should update */
     fun onColorPaletteChanged(resources: Resources) {}
 
@@ -144,14 +147,6 @@
      * 0.0 -> clock is scaled down in the shade; previewRatio is previewSize / screenSize
      */
     fun onPickerCarouselSwiping(swipingFraction: Float, previewRatio: Float) {}
-
-    /**
-     * Whether this clock has a custom position update animation. If true, the keyguard will call
-     * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
-     * animation will be used (e.g. a simple translation).
-     */
-    val hasCustomPositionUpdatedAnimation
-        get() = false
 }
 
 /** Events that have specific data about the related face */
@@ -159,14 +154,6 @@
     /** Call every time tick */
     fun onTimeTick() {}
 
-    /** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */
-    val tickRate: ClockTickRate
-        get() = ClockTickRate.PER_MINUTE
-
-    /** Call to check whether the clock consumes weather data */
-    val hasCustomWeatherDataDisplay: Boolean
-        get() = false
-
     /**
      * Region Darkness specific to the clock face.
      * - isRegionDark = dark theme -> clock should be light
@@ -203,6 +190,28 @@
     val name: String,
 )
 
+/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
+data class ClockConfig(
+    /**
+     * Whether this clock has a custom position update animation. If true, the keyguard will call
+     * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
+     * animation will be used (e.g. a simple translation).
+     */
+    val hasCustomPositionUpdatedAnimation: Boolean = false,
+
+    /** True if the clock will react to tone changes in the seed color. */
+    val isReactiveToTone: Boolean = true,
+)
+
+/** Render configuration options for a clock face. Modifies the way SystemUI behaves. */
+data class ClockFaceConfig(
+    /** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */
+    val tickRate: ClockTickRate = ClockTickRate.PER_MINUTE,
+
+    /** Call to check whether the clock consumes weather data */
+    val hasCustomWeatherDataDisplay: Boolean = false,
+)
+
 /** Structure for keeping clock-specific settings */
 @Keep
 data class ClockSettings(
diff --git a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml b/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml
new file mode 100644
index 0000000..4181220
--- /dev/null
+++ b/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 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.
+*/
+-->
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+
+    <item android:state_selected="true">
+        <shape android:shape="rectangle">
+            <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
+            <stroke
+                android:color="?androidprv:attr/colorAccentPrimary"
+                android:width="@dimen/contrast_dialog_button_stroke_width" />
+            <corners android:radius="@dimen/contrast_dialog_button_radius"/>
+        </shape>
+    </item>
+
+    <item>
+        <layer-list>
+            <item android:top="@dimen/contrast_dialog_button_stroke_width"
+                android:bottom="@dimen/contrast_dialog_button_stroke_width"
+                android:left="@dimen/contrast_dialog_button_stroke_width"
+                android:right="@dimen/contrast_dialog_button_stroke_width">
+                <shape android:shape="rectangle">
+                    <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
+                    <corners android:radius="@dimen/contrast_dialog_button_radius"/>
+                </shape>
+            </item>
+        </layer-list>
+    </item>
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_high.xml b/packages/SystemUI/res/drawable/ic_contrast_high.xml
new file mode 100644
index 0000000..aa5b5ab
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_contrast_high.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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 android:autoMirrored="true" android:height="20dp"
+    android:viewportHeight="20" android:viewportWidth="66"
+    android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#F2F1E8"
+        android:pathData="M0.5,8C0.5,3.858 3.858,0.5 8,0.5H58C62.142,0.5 65.5,3.858 65.5,8V12C65.5,16.142 62.142,19.5 58,19.5H8C3.858,19.5 0.5,16.142 0.5,12V8Z"
+        android:strokeColor="#1B1C17" android:strokeWidth="1"/>
+    <path android:fillColor="#1B1C17" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
+    <path android:fillColor="#1B1C17" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
+    <path android:fillColor="#1B1C17" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_medium.xml b/packages/SystemUI/res/drawable/ic_contrast_medium.xml
new file mode 100644
index 0000000..89519b8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_contrast_medium.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector android:autoMirrored="true" android:height="20dp"
+    android:viewportHeight="20" android:viewportWidth="66"
+    android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#F2F1E8" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/>
+    <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
+    <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
+    <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_standard.xml b/packages/SystemUI/res/drawable/ic_contrast_standard.xml
new file mode 100644
index 0000000..f914975
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_contrast_standard.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector android:autoMirrored="true" android:height="20dp"
+    android:viewportHeight="20" android:viewportWidth="66"
+    android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#C7C8B7" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/>
+    <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
+    <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
+    <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/contrast_dialog.xml b/packages/SystemUI/res/layout/contrast_dialog.xml
new file mode 100644
index 0000000..8e885cf
--- /dev/null
+++ b/packages/SystemUI/res/layout/contrast_dialog.xml
@@ -0,0 +1,127 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <FrameLayout
+            android:id="@+id/contrast_button_standard"
+            android:layout_width="@dimen/contrast_dialog_button_total_size"
+            android:layout_height="@dimen/contrast_dialog_button_total_size"
+            android:background="@drawable/contrast_dialog_button_background">
+
+            <ImageView
+                android:layout_gravity="center"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:src="@drawable/ic_contrast_standard"/>
+        </FrameLayout>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
+            android:gravity="center_horizontal|top"
+            android:textSize="@dimen/contrast_dialog_button_text_size"
+            android:text="@string/quick_settings_contrast_standard"
+            android:textColor="?androidprv:attr/textColorPrimary"/>
+    </LinearLayout>
+
+    <Space
+        android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing"
+        android:layout_height="match_parent" />
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <FrameLayout
+            android:id="@+id/contrast_button_medium"
+            android:layout_width="@dimen/contrast_dialog_button_total_size"
+            android:layout_height="@dimen/contrast_dialog_button_total_size"
+            android:background="@drawable/contrast_dialog_button_background">
+
+            <ImageView
+                android:layout_gravity="center"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:src="@drawable/ic_contrast_medium"/>
+        </FrameLayout>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
+            android:gravity="center_horizontal|top"
+            android:textSize="@dimen/contrast_dialog_button_text_size"
+            android:text="@string/quick_settings_contrast_medium"
+            android:textColor="?androidprv:attr/textColorPrimary"/>
+    </LinearLayout>
+
+    <Space
+        android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing"
+        android:layout_height="match_parent" />
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <FrameLayout
+            android:id="@+id/contrast_button_high"
+            android:layout_width="@dimen/contrast_dialog_button_total_size"
+            android:layout_height="@dimen/contrast_dialog_button_total_size"
+            android:background="@drawable/contrast_dialog_button_background">
+
+            <ImageView
+                android:layout_gravity="center"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:src="@drawable/ic_contrast_high"/>
+
+        </FrameLayout>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
+            android:gravity="center_horizontal|top"
+            android:textSize="@dimen/contrast_dialog_button_text_size"
+            android:text="@string/quick_settings_contrast_high"
+            android:textColor="?androidprv:attr/textColorPrimary"/>
+    </LinearLayout>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2663ffb..714d495 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1735,6 +1735,15 @@
     <dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
     <dimen name="broadcast_dialog_margin">16dp</dimen>
 
+    <!-- Contrast dialog -->
+    <dimen name="contrast_dialog_button_total_size">90dp</dimen>
+    <dimen name="contrast_dialog_button_inner_size">82dp</dimen>
+    <dimen name="contrast_dialog_button_radius">20dp</dimen>
+    <dimen name="contrast_dialog_button_stroke_width">4dp</dimen>
+    <dimen name="contrast_dialog_button_text_size">14sp</dimen>
+    <dimen name="contrast_dialog_button_text_spacing">4dp</dimen>
+    <dimen name="contrast_dialog_button_horizontal_spacing">16dp</dimen>
+
     <!-- Shadow for dream overlay clock complication -->
     <dimen name="dream_overlay_clock_key_text_shadow_dx">0dp</dimen>
     <dimen name="dream_overlay_clock_key_text_shadow_dy">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1dd12ee..f1777f8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -757,6 +757,15 @@
     <!-- 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>
 
+    <!-- QuickSettings: Contrast tile [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_contrast_label">Contrast</string>
+    <!-- QuickSettings: Contrast tile description: standard [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_contrast_standard">Standard</string>
+    <!-- QuickSettings: Contrast tile description: medium [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_contrast_medium">Medium</string>
+    <!-- QuickSettings: Contrast tile description: high [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_contrast_high">High</string>
+
     <!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
     <string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
     <!--- Title of dialog triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=150] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8a86fd5..064cea1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -377,6 +377,10 @@
         <item name="android:windowBackground">@android:color/transparent</item>
     </style>
 
+    <style name="Theme.SystemUI.ContrastDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
+        <item name="android:windowBackground">@android:color/transparent</item>
+    </style>
+
     <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
         <item name="android:dialogCornerRadius">@dimen/notification_corner_radius</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 3b9060a..0779653 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -46,10 +46,10 @@
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.plugins.ClockFaceController
 import com.android.systemui.plugins.ClockTickRate
+import com.android.systemui.plugins.WeatherData
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel.DEBUG
 import com.android.systemui.shared.regionsampling.RegionSampler
-import com.android.systemui.plugins.WeatherData
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -144,8 +144,10 @@
                 val currentViewRect = Rect(left, top, right, bottom)
                 val oldViewRect = Rect(oldLeft, oldTop, oldRight, oldBottom)
 
-                if (currentViewRect.width() != oldViewRect.width() ||
-                    currentViewRect.height() != oldViewRect.height()) {
+                if (
+                    currentViewRect.width() != oldViewRect.width() ||
+                        currentViewRect.height() != oldViewRect.height()
+                ) {
                     updateRegionSampler(view)
                 }
             }
@@ -425,7 +427,7 @@
             }
 
             isRunning = true
-            when (clockFace.events.tickRate) {
+            when (clockFace.config.tickRate) {
                 ClockTickRate.PER_MINUTE -> {
                     /* Handled by KeyguardClockSwitchController */
                 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 07333f7..9290220 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -40,7 +40,6 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.log.dagger.KeyguardClockLog;
-import com.android.systemui.plugins.ClockAnimations;
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.plugins.log.LogBuffer;
 import com.android.systemui.plugins.log.LogLevel;
@@ -469,7 +468,7 @@
     }
 
     @Nullable
-    private ClockController getClock() {
+    public ClockController getClock() {
         return mClockEventController.getClock();
     }
 
@@ -535,13 +534,6 @@
         }
 
         return ((mCurrentClockSize == LARGE) ? clock.getLargeClock() : clock.getSmallClock())
-                .getEvents().getHasCustomWeatherDataDisplay();
-    }
-
-    /** Gets the animations for the current clock. */
-    @Nullable
-    public ClockAnimations getClockAnimations() {
-        ClockController clock = getClock();
-        return clock == null ? null : clock.getAnimations();
+                .getConfig().getHasCustomWeatherDataDisplay();
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index fd55d69..c4df836 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -16,12 +16,13 @@
 
 package com.android.keyguard;
 
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.util.Slog;
 
 import com.android.keyguard.KeyguardClockSwitch.ClockSize;
 import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.plugins.ClockAnimations;
+import com.android.systemui.plugins.ClockController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -241,8 +242,9 @@
         }
     }
 
-    /** Gets the animations for the current clock. */
-    public ClockAnimations getClockAnimations() {
-        return mKeyguardClockSwitchController.getClockAnimations();
+    /** Gets the current clock controller. */
+    @Nullable
+    public ClockController getClockController() {
+        return mKeyguardClockSwitchController.getClock();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 178cda4..ba8e60a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -306,7 +306,7 @@
                 lp.width = mSensorBounds.width();
                 lp.height = mSensorBounds.height();
                 RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
-                lp.setMargins(
+                lp.setMarginsRelative(
                         (int) relativeToView.left,
                         (int) relativeToView.top,
                         (int) relativeToView.right,
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
new file mode 100644
index 0000000..9e15c7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.contrast
+
+import android.app.UiModeManager
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD
+import android.app.UiModeManager.ContrastUtils.fromContrastLevel
+import android.app.UiModeManager.ContrastUtils.toContrastLevel
+import android.content.Context
+import android.os.Bundle
+import android.provider.Settings
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.settings.SecureSettings
+import java.util.concurrent.Executor
+
+/** Dialog to select contrast options */
+class ContrastDialog(
+    context: Context?,
+    @Main private val mainExecutor: Executor,
+    private val uiModeManager: UiModeManager,
+    private val userTracker: UserTracker,
+    private val secureSettings: SecureSettings,
+) : SystemUIDialog(context), UiModeManager.ContrastChangeListener {
+
+    @VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout>
+    lateinit var dialogView: View
+    @VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD)
+
+    public override fun onCreate(savedInstanceState: Bundle?) {
+        dialogView = LayoutInflater.from(context).inflate(R.layout.contrast_dialog, null)
+        setView(dialogView)
+
+        setTitle(R.string.quick_settings_contrast_label)
+        setNeutralButton(R.string.cancel) { _, _ ->
+            secureSettings.putFloatForUser(
+                Settings.Secure.CONTRAST_LEVEL,
+                initialContrast,
+                userTracker.userId
+            )
+            dismiss()
+        }
+        setPositiveButton(R.string.done) { _, _ -> dismiss() }
+        super.onCreate(savedInstanceState)
+
+        contrastButtons =
+            mapOf(
+                CONTRAST_LEVEL_STANDARD to findViewById(R.id.contrast_button_standard),
+                CONTRAST_LEVEL_MEDIUM to findViewById(R.id.contrast_button_medium),
+                CONTRAST_LEVEL_HIGH to findViewById(R.id.contrast_button_high)
+            )
+
+        contrastButtons.forEach { (contrastLevel, contrastButton) ->
+            contrastButton.setOnClickListener {
+                val contrastValue = fromContrastLevel(contrastLevel)
+                secureSettings.putFloatForUser(
+                    Settings.Secure.CONTRAST_LEVEL,
+                    contrastValue,
+                    userTracker.userId
+                )
+            }
+        }
+
+        initialContrast = uiModeManager.contrast
+        highlightContrast(toContrastLevel(initialContrast))
+    }
+
+    override fun onStart() {
+        super.onStart()
+        uiModeManager.addContrastChangeListener(mainExecutor, this)
+    }
+
+    override fun onStop() {
+        super.onStop()
+        uiModeManager.removeContrastChangeListener(this)
+    }
+
+    override fun onContrastChanged(contrast: Float) {
+        highlightContrast(toContrastLevel(contrast))
+    }
+
+    private fun highlightContrast(contrast: Int) {
+        contrastButtons.forEach { (level, button) -> button.isSelected = level == contrast }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt
new file mode 100644
index 0000000..70d7138
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.contrast
+
+import android.app.Activity
+import android.app.UiModeManager
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.SecureSettings
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/** Trampoline activity responsible for creating a [ContrastDialog] */
+class ContrastDialogActivity
+@Inject
+constructor(
+    private val context: Context,
+    @Main private val mainExecutor: Executor,
+    private val uiModeManager: UiModeManager,
+    private val userTracker: UserTracker,
+    private val secureSettings: SecureSettings
+) : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val contrastDialog =
+            ContrastDialog(context, mainExecutor, uiModeManager, userTracker, secureSettings)
+        contrastDialog.show()
+        finish()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 3cf26b3..dba353b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 
 import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.contrast.ContrastDialogActivity;
 import com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity;
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.people.PeopleSpaceActivity;
@@ -73,6 +74,12 @@
     @ClassKey(BrightnessDialog.class)
     public abstract Activity bindBrightnessDialog(BrightnessDialog activity);
 
+    /** Inject into ContrastDialogActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(ContrastDialogActivity.class)
+    public abstract Activity bindContrastDialogActivity(ContrastDialogActivity activity);
+
     /** Inject into UsbDebuggingActivity. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 503b6fc..0bc8506 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -285,7 +285,7 @@
     /** Enables Font Scaling Quick Settings tile */
     // TODO(b/269341316): Tracking Bug
     @JvmField
-    val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = true)
+    val ENABLE_FONT_SCALING_TILE = releasedFlag(509, "enable_font_scaling_tile")
 
     /** Enables new QS Edit Mode visual refresh */
     // TODO(b/269787742): Tracking Bug
@@ -417,7 +417,7 @@
 
     // TODO(b/273509374): Tracking Bug
     @JvmField
-    val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = unreleasedFlag(1006,
+    val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = releasedFlag(1006,
         "always_show_home_controls_on_dreams")
 
     // 1100 - windowing
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b1efdd7..c102c5b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2856,14 +2856,14 @@
                         + " wasShowing=" + wasShowing);
             }
 
+            mKeyguardUnlockAnimationControllerLazy.get()
+                    .notifyFinishedKeyguardExitAnimation(cancelled);
             finishSurfaceBehindRemoteAnimation(cancelled);
 
             // Dispatch the callback on animation finishes.
             mUpdateMonitor.dispatchKeyguardDismissAnimationFinished();
         });
 
-        mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation(
-                cancelled);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 8f1c904..30ee147 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -63,6 +63,10 @@
                 override fun onStateChanged(newState: Int) {
                     refreshMediaPosition()
                 }
+
+                override fun onDozingChanged(isDozing: Boolean) {
+                    refreshMediaPosition()
+                }
             }
         )
         configurationController.addCallback(
@@ -198,7 +202,8 @@
             mediaHost.visible &&
                 !bypassController.bypassEnabled &&
                 keyguardOrUserSwitcher &&
-                allowMediaPlayerOnLockScreen
+                allowMediaPlayerOnLockScreen &&
+                shouldBeVisibleForSplitShade()
         if (visible) {
             showMediaPlayer()
         } else {
@@ -206,6 +211,19 @@
         }
     }
 
+    private fun shouldBeVisibleForSplitShade(): Boolean {
+        if (!useSplitShade) {
+            return true
+        }
+        // We have to explicitly hide media for split shade when on AOD, as it is a child view of
+        // keyguard status view, and nothing hides keyguard status view on AOD.
+        // When using the double-line clock, it is not an issue, as media gets implicitly hidden
+        // by the clock. This is not the case for single-line clock though.
+        // For single shade, we don't need to do it, because media is a child of NSSL, which already
+        // gets hidden on AOD.
+        return !statusBarStateController.isDozing
+    }
+
     private fun showMediaPlayer() {
         if (useSplitShade) {
             setVisibility(splitShadeContainer, View.VISIBLE)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 9928c4f..f50a7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -205,7 +205,8 @@
                         && mController.isSubStatusSupported()
                         && mController.isAdvancedLayoutSupported() && device.hasSubtext()) {
                     boolean isActiveWithOngoingSession =
-                            (device.hasOngoingSession() && currentlyConnected);
+                            (device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded(
+                                    mController.getSelectedMediaDevice(), device)));
                     boolean isHost = device.isHostForOngoingSession()
                             && isActiveWithOngoingSession;
                     if (isHost) {
@@ -224,10 +225,17 @@
                         if (isActiveWithOngoingSession) {
                             //Selected device which has ongoing session, disable seekbar since we
                             //only allow volume control on Host
-                            initSeekbar(device, isCurrentSeekbarInvisible);
                             mCurrentActivePosition = position;
                         }
-                        setUpDeviceIcon(device);
+                        boolean showSeekbar =
+                                (!device.hasOngoingSession() && currentlyConnected);
+                        if (showSeekbar) {
+                            updateTitleIcon(R.drawable.media_output_icon_volume,
+                                    mController.getColorItemContent());
+                            initSeekbar(device, isCurrentSeekbarInvisible);
+                        } else {
+                            setUpDeviceIcon(device);
+                        }
                         mSubTitleText.setText(device.getSubtextString());
                         Drawable deviceStatusIcon =
                                 device.hasOngoingSession() ? mContext.getDrawable(
@@ -241,8 +249,8 @@
                         updateTwoLineLayoutContentAlpha(
                                 updateClickActionBasedOnSelectionBehavior(device)
                                         ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
-                        setTwoLineLayout(device, isActiveWithOngoingSession /* bFocused */,
-                                isActiveWithOngoingSession /* showSeekBar */,
+                        setTwoLineLayout(device, currentlyConnected /* bFocused */,
+                                showSeekbar  /* showSeekBar */,
                                 false /* showProgressBar */, true /* showSubtitle */,
                                 deviceStatusIcon != null /* showStatus */,
                                 isActiveWithOngoingSession /* isFakeActive */);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 731bb2f..73ab5272 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -270,10 +270,10 @@
             final Drawable backgroundDrawable;
             if (mController.isAdvancedLayoutSupported() && mController.isSubStatusSupported()) {
                 backgroundDrawable = mContext.getDrawable(
-                        showSeekBar ? R.drawable.media_output_item_background_active
+                        showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active
                                 : R.drawable.media_output_item_background).mutate();
                 backgroundDrawable.setTint(
-                        showSeekBar ? mController.getColorConnectedItemBackground()
+                        showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground()
                                 : mController.getColorItemBackground());
                 mIconAreaLayout.setBackgroundTintList(
                         ColorStateList.valueOf(showProgressBar || isFakeActive
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b7243ae9..79d3b26 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -162,7 +162,7 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.plugins.ClockAnimations;
+import com.android.systemui.plugins.ClockController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
 import com.android.systemui.plugins.qs.QS;
@@ -1627,9 +1627,9 @@
                 transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
                 transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
 
-                ClockAnimations clockAnims = mKeyguardStatusViewController.getClockAnimations();
-                boolean customClockAnimation = clockAnims != null
-                        && clockAnims.getHasCustomPositionUpdatedAnimation();
+                ClockController clock = mKeyguardStatusViewController.getClockController();
+                boolean customClockAnimation = clock != null
+                        && clock.getConfig().getHasCustomPositionUpdatedAnimation();
 
                 if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
                     // Find the clock, so we can exclude it from this transition.
@@ -5159,12 +5159,12 @@
             Rect to = (Rect) endValues.values.get(PROP_BOUNDS);
 
             anim.addUpdateListener(animation -> {
-                ClockAnimations clockAnims = mController.getClockAnimations();
-                if (clockAnims == null) {
+                ClockController clock = mController.getClockController();
+                if (clock == null) {
                     return;
                 }
 
-                clockAnims.onPositionUpdated(from, to, animation.getAnimatedFraction());
+                clock.getAnimations().onPositionUpdated(from, to, animation.getAnimatedFraction());
             });
 
             return anim;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 0fd007c..62bc27f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -170,7 +170,10 @@
                     if (networkTypeIconGroup.dataContentDescription != 0)
                         ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
                     else null
-                val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
+                val icon =
+                    if (networkTypeIconGroup.dataType != 0)
+                        Icon.Resource(networkTypeIconGroup.dataType, desc)
+                    else null
                 return@combine when {
                     !shouldShow -> null
                     else -> icon
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index c6da55c..968dcc9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.UserTracker;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -108,6 +109,7 @@
 
     private final Context mContext;
     private final Intent mServiceIntent;
+    private final UserTracker mUserTracker;
     private final int mFlags;
     private final Executor mExecutor;
     private final ServiceTransformer<T> mTransformer;
@@ -127,10 +129,12 @@
      */
     @Inject
     public ObservableServiceConnection(Context context, Intent serviceIntent,
+            UserTracker userTracker,
             @Main Executor executor,
             ServiceTransformer<T> transformer) {
         mContext = context;
         mServiceIntent = serviceIntent;
+        mUserTracker = userTracker;
         mFlags = Context.BIND_AUTO_CREATE;
         mExecutor = executor;
         mTransformer = transformer;
@@ -145,7 +149,8 @@
     public boolean bind() {
         boolean bindResult = false;
         try {
-            bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+            bindResult = mContext.bindServiceAsUser(mServiceIntent, this, mFlags,
+                    mUserTracker.getUserHandle());
         } catch (SecurityException e) {
             Log.d(TAG, "Could not bind to service", e);
             mContext.unbindService(this);
@@ -228,11 +233,13 @@
 
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
-        if (DEBUG) {
-            Log.d(TAG, "onServiceConnected");
-        }
-        mProxy = mTransformer.convert(service);
-        applyToCallbacksLocked(callback -> callback.onConnected(this, mProxy));
+        mExecutor.execute(() -> {
+            if (DEBUG) {
+                Log.d(TAG, "onServiceConnected");
+            }
+            mProxy = mTransformer.convert(service);
+            applyToCallbacksLocked(callback -> callback.onConnected(this, mProxy));
+        });
     }
 
     private void applyToCallbacksLocked(Consumer<Callback<T>> applicator) {
@@ -250,16 +257,16 @@
 
     @Override
     public void onServiceDisconnected(ComponentName name) {
-        onDisconnected(DISCONNECT_REASON_DISCONNECTED);
+        mExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_DISCONNECTED));
     }
 
     @Override
     public void onBindingDied(ComponentName name) {
-        onDisconnected(DISCONNECT_REASON_DISCONNECTED);
+        mExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_BINDING_DIED));
     }
 
     @Override
     public void onNullBinding(ComponentName name) {
-        onDisconnected(DISCONNECT_REASON_NULL_BINDING);
+        mExecutor.execute(() -> onDisconnected(DISCONNECT_REASON_NULL_BINDING));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 480b8f9..a9920ec7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.plugins.ClockEvents
 import com.android.systemui.plugins.ClockFaceController
+import com.android.systemui.plugins.ClockFaceConfig
 import com.android.systemui.plugins.ClockFaceEvents
 import com.android.systemui.plugins.ClockTickRate
 import com.android.systemui.plugins.log.LogBuffer
@@ -101,8 +102,10 @@
         whenever(largeClockController.events).thenReturn(largeClockEvents)
         whenever(clock.events).thenReturn(events)
         whenever(clock.animations).thenReturn(animations)
-        whenever(smallClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE)
-        whenever(largeClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE)
+        whenever(smallClockController.config)
+            .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
+        whenever(largeClockController.config)
+            .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
 
         repository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b15ac39..2f627cb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -315,8 +315,8 @@
     }
 
     @Test
-    public void testGetClockAnimationsForwardsToClock() {
-        assertEquals(mClockAnimations, mController.getClockAnimations());
+    public void testGetClock_ForwardsToClock() {
+        assertEquals(mClockController, mController.getClock());
     }
 
     @Test
@@ -367,9 +367,9 @@
     }
 
     @Test
-    public void testGetClockAnimations_nullClock_returnsNull() {
+    public void testGetClock_nullClock_returnsNull() {
         when(mClockEventController.getClock()).thenReturn(null);
-        assertNull(mController.getClockAnimations());
+        assertNull(mController.getClock());
     }
 
     private void verifyAttachment(VerificationMode times) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 7144914..48f7d92 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -16,17 +16,17 @@
 
 package com.android.keyguard;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.graphics.Rect;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ClockAnimations;
+import com.android.systemui.plugins.ClockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -118,14 +118,10 @@
     }
 
     @Test
-    public void getClockAnimations_forwardsToClockSwitch() {
-        ClockAnimations mockClockAnimations = mock(ClockAnimations.class);
-        when(mKeyguardClockSwitchController.getClockAnimations()).thenReturn(mockClockAnimations);
+    public void getClock_forwardsToClockSwitch() {
+        ClockController mockClock = mock(ClockController.class);
+        when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
 
-        Rect r1 = new Rect(1, 2, 3, 4);
-        Rect r2 = new Rect(5, 6, 7, 8);
-        mController.getClockAnimations().onPositionUpdated(r1, r2, 0.3f);
-
-        verify(mockClockAnimations).onPositionUpdated(r1, r2, 0.3f);
+        assertEquals(mockClock, mController.getClockController());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogTest.kt
new file mode 100644
index 0000000..7753197
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.contrast
+
+import android.app.UiModeManager
+import android.app.UiModeManager.ContrastUtils.fromContrastLevel
+import android.os.Looper
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/** Test the behaviour of buttons of the [ContrastDialog]. */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ContrastDialogTest : SysuiTestCase() {
+
+    private lateinit var mainExecutor: Executor
+    private lateinit var contrastDialog: ContrastDialog
+    @Mock private lateinit var mockUiModeManager: UiModeManager
+    @Mock private lateinit var mockUserTracker: UserTracker
+    @Mock private lateinit var mockSecureSettings: SecureSettings
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mainExecutor = MoreExecutors.directExecutor()
+        whenever(mockUserTracker.userId).thenReturn(context.userId)
+    }
+
+    @Test
+    fun testClickButtons_putsContrastInSettings() {
+        if (Looper.myLooper() == null) Looper.prepare()
+        contrastDialog =
+            ContrastDialog(
+                context,
+                mainExecutor,
+                mockUiModeManager,
+                mockUserTracker,
+                mockSecureSettings
+            )
+        contrastDialog.show()
+        try {
+            contrastDialog.contrastButtons.forEach {
+                (contrastLevel: Int, clickedButton: FrameLayout) ->
+                clickedButton.performClick()
+                verify(mockSecureSettings)
+                    .putFloatForUser(
+                        eq(Settings.Secure.CONTRAST_LEVEL),
+                        eq(fromContrastLevel(contrastLevel)),
+                        eq(context.userId)
+                    )
+            }
+        } finally {
+            contrastDialog.dismiss()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 6884616..f4cc8bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -60,6 +60,12 @@
             }
         val TEST_STRUCTURE: CharSequence = "TestStructure"
         val TEST_APP: CharSequence = "TestApp"
+
+        private fun View.waitForPost() {
+            val latch = CountDownLatch(1)
+            post(latch::countDown)
+            latch.await()
+        }
     }
 
     @Main private val executor: Executor = MoreExecutors.directExecutor()
@@ -140,7 +146,10 @@
             val rearrangeButton = requireViewById<Button>(R.id.rearrange)
             assertThat(rearrangeButton.visibility).isEqualTo(View.VISIBLE)
             assertThat(rearrangeButton.isEnabled).isFalse()
-            assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.GONE)
+
+            val otherAppsButton = requireViewById<Button>(R.id.other_apps)
+            otherAppsButton.waitForPost()
+            assertThat(otherAppsButton.visibility).isEqualTo(View.GONE)
         }
     }
 
@@ -160,7 +169,10 @@
             val rearrangeButton = requireViewById<Button>(R.id.rearrange)
             assertThat(rearrangeButton.visibility).isEqualTo(View.GONE)
             assertThat(rearrangeButton.isEnabled).isFalse()
-            assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.VISIBLE)
+
+            val otherAppsButton = requireViewById<Button>(R.id.other_apps)
+            otherAppsButton.waitForPost()
+            assertThat(otherAppsButton.visibility).isEqualTo(View.VISIBLE)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index 2026006..b40ebc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -24,12 +24,14 @@
 import android.view.View.VISIBLE
 import android.widget.FrameLayout
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.animation.UniqueObjectHostView
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.utils.os.FakeHandler
 import com.google.common.truth.Truth.assertThat
@@ -39,8 +41,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
@@ -61,9 +64,16 @@
     private lateinit var keyguardMediaController: KeyguardMediaController
     private lateinit var testableLooper: TestableLooper
     private lateinit var fakeHandler: FakeHandler
+    private lateinit var statusBarStateListener: StatusBarStateController.StateListener
 
     @Before
     fun setup() {
+        doAnswer {
+                statusBarStateListener = it.arguments[0] as StatusBarStateController.StateListener
+                return@doAnswer Unit
+            }
+            .whenever(statusBarStateController)
+            .addCallback(any(StatusBarStateController.StateListener::class.java))
         // default state is positive, media should show up
         whenever(mediaHost.visible).thenReturn(true)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
@@ -170,4 +180,31 @@
     fun testMediaHost_expandedPlayer() {
         verify(mediaHost).expansion = MediaHostState.EXPANDED
     }
+
+    @Test
+    fun dozing_inSplitShade_mediaIsHidden() {
+        val splitShadeContainer = FrameLayout(context)
+        keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+        keyguardMediaController.useSplitShade = true
+
+        setDozing()
+
+        assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
+    }
+
+    @Test
+    fun dozing_inSingleShade_mediaIsVisible() {
+        val splitShadeContainer = FrameLayout(context)
+        keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+        keyguardMediaController.useSplitShade = false
+
+        setDozing()
+
+        assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
+    }
+
+    private fun setDozing() {
+        whenever(statusBarStateController.isDozing).thenReturn(true)
+        statusBarStateListener.onDozingChanged(true)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 17d8799..7f7952f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -532,7 +532,7 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 8ea8f87..1593e5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -20,6 +20,7 @@
 import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
 import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
 import com.android.settingslib.mobile.TelephonyIcons.THREE_G
+import com.android.settingslib.mobile.TelephonyIcons.UNKNOWN
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -343,7 +344,7 @@
     fun networkType_alwaysShow_shownEvenWhenDisabled() =
         testScope.runTest {
             interactor.setIconGroup(THREE_G)
-            interactor.setIsDataEnabled(true)
+            interactor.setIsDataEnabled(false)
             interactor.alwaysShowDataRatIcon.value = true
 
             var latest: Icon? = null
@@ -400,6 +401,21 @@
         }
 
     @Test
+    fun networkType_alwaysShow_notShownWhenInvalidDataTypeIcon() =
+        testScope.runTest {
+            // The UNKNOWN icon group doesn't have a valid data type icon ID
+            interactor.setIconGroup(UNKNOWN)
+            interactor.alwaysShowDataRatIcon.value = true
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
     fun `network type - alwaysShow - shown when not default`() =
         testScope.runTest {
             interactor.setIconGroup(THREE_G)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index f9bfafc..766a5ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -29,12 +29,15 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeUserTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -44,6 +47,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
 import java.util.Objects;
 
 @SmallTest
@@ -93,15 +97,22 @@
 
     FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
+    private FakeUserTracker mUserTracker;
+
+    private static final int MAIN_USER_ID = 10;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mUserTracker = new FakeUserTracker();
+        // Set the main user as the current user.
+        mUserTracker.set(List.of(new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_MAIN)), 0);
     }
 
     @Test
     public void testConnect() {
         ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
-                mIntent, mExecutor, mTransformer);
+                mIntent, mUserTracker, mExecutor, mTransformer);
         // Register twice to ensure only one callback occurs.
         connection.addCallback(mCallback);
         connection.addCallback(mCallback);
@@ -121,15 +132,16 @@
     @Test
     public void testDisconnect() {
         ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
-                mIntent, mExecutor, mTransformer);
+                mIntent, mUserTracker, mExecutor, mTransformer);
         connection.addCallback(mCallback);
         connection.onServiceDisconnected(mComponentName);
+        mExecutor.runAllReady();
 
         // Disconnects before binds should be ignored.
         verify(mCallback, never()).onDisconnected(eq(connection), anyInt());
 
-        when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
-                .thenReturn(true);
+        when(mContext.bindServiceAsUser(eq(mIntent), eq(connection), anyInt(),
+                eq(UserHandle.of(MAIN_USER_ID)))).thenReturn(true);
         connection.bind();
         connection.onServiceDisconnected(mComponentName);
 
@@ -151,15 +163,16 @@
     @Test
     public void testUnbind() {
         ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
-                mIntent, mExecutor, mTransformer);
+                mIntent, mUserTracker, mExecutor, mTransformer);
         connection.addCallback(mCallback);
         connection.onServiceDisconnected(mComponentName);
+        mExecutor.runAllReady();
 
         // Disconnects before binds should be ignored.
         verify(mCallback, never()).onDisconnected(eq(connection), anyInt());
 
-        when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
-                .thenReturn(true);
+        when(mContext.bindServiceAsUser(eq(mIntent), eq(connection), anyInt(),
+                eq(UserHandle.of(MAIN_USER_ID)))).thenReturn(true);
         connection.bind();
 
         mExecutor.runAllReady();
@@ -175,10 +188,11 @@
     @Test
     public void testBindServiceThrowsError() {
         ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
-                mIntent, mExecutor, mTransformer);
+                mIntent, mUserTracker, mExecutor, mTransformer);
         connection.addCallback(mCallback);
 
-        when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
+        when(mContext.bindServiceAsUser(eq(mIntent), eq(connection), anyInt(),
+                eq(UserHandle.of(MAIN_USER_ID))))
                 .thenThrow(new SecurityException());
 
         // Verify that the exception was caught and that bind returns false, and we properly
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
new file mode 100644
index 0000000..ffb4632
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.Optional;
+
+/**
+ * Helper class to log Field Classification stats.
+ */
+public final class FieldClassificationEventLogger {
+    private static final String TAG = "FieldClassificationEventLogger";
+    private Optional<FieldClassificationEventInternal> mEventInternal;
+
+    private FieldClassificationEventLogger() {
+        mEventInternal = Optional.empty();
+    }
+
+    /**
+     * A factory constructor to create FieldClassificationEventLogger.
+     */
+    public static FieldClassificationEventLogger createLogger() {
+        return new FieldClassificationEventLogger();
+    }
+
+    /**
+     * Reset mEventInternal before logging for a new request. It shall be called for each
+     * FieldClassification request.
+     */
+    public void startNewLogForRequest() {
+        if (!mEventInternal.isEmpty()) {
+            Slog.w(TAG, "FieldClassificationEventLogger is not empty before starting for a new "
+                    + "request");
+        }
+        mEventInternal = Optional.of(new FieldClassificationEventInternal());
+    }
+
+    /**
+     * Set latency as long as mEventInternal presents.
+     */
+    public void maybeSetLatencyMillis(long timestamp) {
+        mEventInternal.ifPresent(event -> {
+            event.mLatencyClassificationRequestMillis = timestamp;
+        });
+    }
+
+    /**
+     * Log an AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED event.
+     */
+    public void logAndEndEvent() {
+        if (!mEventInternal.isPresent()) {
+            Slog.w(TAG, "Shouldn't be logging AutofillFieldClassificationEventInternal again for "
+                    + "same event");
+            return;
+        }
+        FieldClassificationEventInternal event = mEventInternal.get();
+        if (sVerbose) {
+            Slog.v(TAG, "Log AutofillFieldClassificationEventReported:"
+                    + " mLatencyClassificationRequestMillis="
+                    + event.mLatencyClassificationRequestMillis);
+        }
+        FrameworkStatsLog.write(
+                AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED,
+                event.mLatencyClassificationRequestMillis);
+        mEventInternal = Optional.empty();
+    }
+
+    private static final class FieldClassificationEventInternal {
+        long mLatencyClassificationRequestMillis = -1;
+
+        FieldClassificationEventInternal() {
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
index 6b8246c..8f2ab71 100644
--- a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
@@ -17,16 +17,16 @@
 package com.android.server.autofill;
 
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_RESULT_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_SUCCESS;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__AUTHENTICATION_TYPE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__DATASET_AUTHENTICATION;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__FULL_AUTHENTICATION;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_RESULT_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_FAILURE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_CANCELLED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_FAILURE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_SESSION_DESTROYED;
@@ -37,12 +37,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.provider.Settings;
 import android.service.autofill.Dataset;
-import android.text.TextUtils;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
 
@@ -154,6 +149,9 @@
   // succeeded.
   public static final int AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT = -1;
 
+  // Log a magic number to indicate that the FillResponse contains a saveTriggerId.
+  public static final int HAVE_SAVE_TRIGGER_ID = 1;
+
   private final int mSessionId;
   private Optional<FillResponseEventInternal> mEventInternal;
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
index 99a2291..feae56e 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
@@ -30,6 +30,7 @@
 import android.content.pm.ServiceInfo;
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.assist.classification.FieldClassificationRequest;
 import android.service.assist.classification.FieldClassificationResponse;
 import android.service.assist.classification.FieldClassificationService;
@@ -132,7 +133,7 @@
 
     public void onFieldClassificationRequest(@NonNull FieldClassificationRequest request,
             FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks) {
-
+        final long startTime = SystemClock.elapsedRealtime();
         if (sVerbose) {
             Slog.v(TAG, "onFieldClassificationRequest request:" + request);
         }
@@ -144,6 +145,7 @@
                                 new IFieldClassificationCallback.Stub() {
                                     @Override
                                     public void onCancellable(ICancellationSignal cancellation) {
+                                        logLatency(startTime);
                                         if (sDebug) {
                                             Log.d(TAG, "onCancellable");
                                         }
@@ -151,15 +153,15 @@
 
                                     @Override
                                     public void onSuccess(FieldClassificationResponse response) {
+                                        logLatency(startTime);
                                         if (sDebug) {
                                             Log.d(TAG, "onSuccess Response: " + response);
                                         }
-                                        fieldClassificationServiceCallbacks
-                                                .onClassificationRequestSuccess(response);
                                     }
 
                                     @Override
                                     public void onFailure() {
+                                        logLatency(startTime);
                                         if (sDebug) {
                                             Log.d(TAG, "onFailure");
                                         }
@@ -174,4 +176,12 @@
                                     public void cancel() throws RemoteException {}
                                 }));
     }
+
+    private void logLatency(long startTime) {
+        final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
+        logger.startNewLogForRequest();
+        logger.maybeSetLatencyMillis(
+                SystemClock.elapsedRealtime() - startTime);
+        logger.logAndEndEvent();
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/SaveEventLogger.java b/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
index 4b7d5bd..e5435c2 100644
--- a/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
@@ -23,8 +23,10 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_NONE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_NO_SAVE_INFO;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_NO_VALUE_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_SESSION_DESTROYED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_SHOWN_REASON__SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_SHOWN_REASON__SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_SHOWN_REASON__SAVE_UI_SHOWN_REASON_TRIGGER_ID_SET;
@@ -32,11 +34,6 @@
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.IntDef;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -74,10 +71,12 @@
       NO_SAVE_REASON_NONE,
       NO_SAVE_REASON_NO_SAVE_INFO,
       NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG,
+      NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG,
       NO_SAVE_REASON_HAS_EMPTY_REQUIRED,
       NO_SAVE_REASON_NO_VALUE_CHANGED,
       NO_SAVE_REASON_FIELD_VALIDATION_FAILED,
-      NO_SAVE_REASON_DATASET_MATCH
+      NO_SAVE_REASON_DATASET_MATCH,
+      NO_SAVE_REASON_SESSION_DESTROYED
   })
   @Retention(RetentionPolicy.SOURCE)
   public @interface SaveUiNotShownReason {
@@ -108,6 +107,10 @@
       AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_FIELD_VALIDATION_FAILED;
   public static final int NO_SAVE_REASON_DATASET_MATCH =
       AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_DATASET_MATCH;
+  public static final int NO_SAVE_REASON_SESSION_DESTROYED =
+      AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_SESSION_DESTROYED;
+  public static final int NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG =
+      AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG;
 
   private final int mSessionId;
   private Optional<SaveEventInternal> mEventInternal;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index fe2a3ba..8d039fc 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -45,6 +45,7 @@
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_PRE_TRIGGER;
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
 import static com.android.server.autofill.FillResponseEventLogger.AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT;
+import static com.android.server.autofill.FillResponseEventLogger.HAVE_SAVE_TRIGGER_ID;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_FAILURE;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SUCCESS;
@@ -58,8 +59,22 @@
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_CHANGED;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_DATASET_MATCH;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_FIELD_VALIDATION_FAILED;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_HAS_EMPTY_REQUIRED;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_NONE;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_NO_SAVE_INFO;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_NO_VALUE_CHANGED;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_SESSION_DESTROYED;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG;
+import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE;
+import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE;
+import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_TRIGGER_ID_SET;
+import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_UNKNOWN;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
@@ -457,6 +472,14 @@
     @GuardedBy("mLock")
     private FillResponseEventLogger mFillResponseEventLogger;
 
+    @NonNull
+    @GuardedBy("mLock")
+    private SaveEventLogger mSaveEventLogger;
+
+    @NonNull
+    @GuardedBy("mLock")
+    private SessionCommittedEventLogger mSessionCommittedEventLogger;
+
     /**
      * Fill dialog request would likely be sent slightly later.
      */
@@ -1327,6 +1350,8 @@
         mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
         mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
         mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
+        mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
+        mSaveEventLogger = SaveEventLogger.forSessionId(sessionId);
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
             mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
@@ -2048,6 +2073,12 @@
     @Override
     public void onSaveRequestSuccess(@NonNull String servicePackageName,
             @Nullable IntentSender intentSender) {
+        // Log onSaveRequest result.
+        mSaveEventLogger.maybeSetIsSaved(true);
+        final long saveRequestFinishTimestamp = SystemClock.elapsedRealtime() - mLatencyBaseTime;
+        mSaveEventLogger.maybeSetLatencySaveFinishMillis(saveRequestFinishTimestamp);
+        mSaveEventLogger.logAndEndEvent();
+
         synchronized (mLock) {
             mSessionFlags.mShowingSaveUi = false;
 
@@ -2060,6 +2091,8 @@
         LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
                 .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN);
         mMetricsLogger.write(log);
+
+
         if (intentSender != null) {
             if (sDebug) Slog.d(TAG, "Starting intent sender on save()");
             startIntentSenderAndFinishSession(intentSender);
@@ -2074,6 +2107,12 @@
     public void onSaveRequestFailure(@Nullable CharSequence message,
             @NonNull String servicePackageName) {
         boolean showMessage = !TextUtils.isEmpty(message);
+
+        // Log onSaveRequest result.
+        final long saveRequestFinishTimestamp = SystemClock.elapsedRealtime() - mLatencyBaseTime;
+        mSaveEventLogger.maybeSetLatencySaveFinishMillis(saveRequestFinishTimestamp);
+        mSaveEventLogger.logAndEndEvent();
+
         synchronized (mLock) {
             mSessionFlags.mShowingSaveUi = false;
 
@@ -2099,6 +2138,7 @@
         }
         mMetricsLogger.write(log);
 
+
         if (showMessage) {
             getUiForShowing().showError(message, this);
         }
@@ -2188,13 +2228,17 @@
     // AutoFillUiCallback
     @Override
     public void save() {
+        mSaveEventLogger.maybeSetSaveButtonClicked(true);
         synchronized (mLock) {
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#save() rejected - session: "
                         + id + " destroyed");
+                mSaveEventLogger.logAndEndEvent();
                 return;
             }
         }
+        final long saveRequestStartTimestamp = SystemClock.elapsedRealtime() - mLatencyBaseTime;
+        mSaveEventLogger.maybeSetLatencySaveRequestMillis(saveRequestStartTimestamp);
         mHandler.sendMessage(obtainMessage(
                 AutofillManagerServiceImpl::handleSessionSave,
                 mService, this));
@@ -2203,12 +2247,14 @@
     // AutoFillUiCallback
     @Override
     public void cancelSave() {
+        mSaveEventLogger.maybeSetDialogDismissed(true);
         synchronized (mLock) {
             mSessionFlags.mShowingSaveUi = false;
 
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
                         + id + " destroyed");
+                mSaveEventLogger.logAndEndEvent();
                 return;
             }
         }
@@ -3031,6 +3077,8 @@
         if (mDestroyed) {
             Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
                     + id + " destroyed");
+            mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED);
+            mSaveEventLogger.logAndEndEvent();
             return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
                     Event.NO_SAVE_UI_REASON_NONE);
         }
@@ -3050,6 +3098,8 @@
          */
         if (saveInfo == null) {
             if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service");
+            mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO);
+            mSaveEventLogger.logAndEndEvent();
             return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
                     Event.NO_SAVE_UI_REASON_NO_SAVE_INFO);
         }
@@ -3057,6 +3107,8 @@
         if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) {
             // TODO(b/113281366): log metrics
             if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save");
+            mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG);
+            mSaveEventLogger.logAndEndEvent();
             return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
                     Event.NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG);
         }
@@ -3130,6 +3182,8 @@
                                         + "didn't change: " + value);
                             }
                             changed = false;
+                        } else {
+                            mSaveEventLogger.maybeSetIsNewField(true);
                         }
                     } else {
                         isUpdate = true;
@@ -3153,6 +3207,9 @@
         int saveDialogNotShowReason;
         if (!allRequiredAreNotEmpty) {
             saveDialogNotShowReason = Event.NO_SAVE_UI_REASON_HAS_EMPTY_REQUIRED;
+
+            mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_HAS_EMPTY_REQUIRED);
+            mSaveEventLogger.logAndEndEvent();
         } else {
             // Must look up all optional ids in 2 scenarios:
             // - if no required id changed but an optional id did, it should trigger save / update
@@ -3188,6 +3245,8 @@
                             }
                             if (filledValue != null) {
                                 isUpdate = true;
+                            } else {
+                                mSaveEventLogger.maybeSetIsNewField(true);
                             }
                             atLeastOneChanged = true;
                         }
@@ -3206,6 +3265,8 @@
             }
             if (!atLeastOneChanged) {
                 saveDialogNotShowReason = Event.NO_SAVE_UI_REASON_NO_VALUE_CHANGED;
+                mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_VALUE_CHANGED);
+                mSaveEventLogger.logAndEndEvent();
             } else {
                 if (sDebug) {
                     Slog.d(TAG, "at least one field changed, validate fields for save UI");
@@ -3224,6 +3285,9 @@
                         Slog.e(TAG, "Not showing save UI because validation failed:", e);
                         log.setType(MetricsEvent.TYPE_FAILURE);
                         mMetricsLogger.write(log);
+                        mSaveEventLogger.maybeSetSaveUiNotShownReason(
+                            NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
+                        mSaveEventLogger.logAndEndEvent();
                         return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
                                 Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED);
                     }
@@ -3231,6 +3295,9 @@
                     mMetricsLogger.write(log);
                     if (!isValid) {
                         Slog.i(TAG, "not showing save UI because fields failed validation");
+                        mSaveEventLogger.maybeSetSaveUiNotShownReason(
+                            NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
+                        mSaveEventLogger.logAndEndEvent();
                         return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
                                 Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED);
                     }
@@ -3271,6 +3338,8 @@
                             Slog.d(TAG, "ignoring Save UI because all fields match contents of "
                                     + "dataset #" + i + ": " + dataset);
                         }
+                        mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_DATASET_MATCH);
+                        mSaveEventLogger.logAndEndEvent();
                         return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
                                 Event.NO_SAVE_UI_REASON_DATASET_MATCH);
                     }
@@ -3292,14 +3361,18 @@
                 }
                 if (serviceLabel == null || serviceIcon == null) {
                     wtf(null, "showSaveLocked(): no service label or icon");
+                    mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
+                    mSaveEventLogger.logAndEndEvent();
                     return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
                             Event.NO_SAVE_UI_REASON_NONE);
                 }
-
+                final long saveUiDisplayStartTimestamp = SystemClock.elapsedRealtime();
                 getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
                         mService.getServicePackageName(), saveInfo, this,
                         mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode,
                         response.getShowSaveDialogIcon());
+                mSaveEventLogger.maybeSetLatencySaveUiDisplayMillis(
+                    SystemClock.elapsedRealtime()- saveUiDisplayStartTimestamp);
                 if (client != null) {
                     try {
                         client.setSaveUiState(id, true);
@@ -3471,11 +3544,15 @@
         if (mDestroyed) {
             Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
                     + id + " destroyed");
+            mSaveEventLogger.maybeSetIsSaved(false);
+            mSaveEventLogger.logAndEndEvent();
             return;
         }
         if (mRemoteFillService == null) {
             wtf(null, "callSaveLocked() called without a remote service. "
                     + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+            mSaveEventLogger.maybeSetIsSaved(false);
+            mSaveEventLogger.logAndEndEvent();
             return;
         }
 
@@ -3483,6 +3560,8 @@
 
         if (mContexts == null) {
             Slog.w(TAG, "callSaveLocked(): no contexts");
+            mSaveEventLogger.maybeSetIsSaved(false);
+            mSaveEventLogger.logAndEndEvent();
             return;
         }
 
@@ -3899,6 +3978,8 @@
                     mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
                     mPresentationStatsEventLogger.maybeSetAvailableCount(
                             response.getDatasets(), mCurrentViewId);
+                    mFillResponseEventLogger.maybeSetAvailableCount(
+                        response.getDatasets(), mCurrentViewId);
                 }
 
                 if (isSameViewEntered) {
@@ -4069,6 +4150,11 @@
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
                         + id + " destroyed");
+                mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED);
+                mSaveEventLogger.logAndEndEvent();
+                mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                    NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
+                mPresentationStatsEventLogger.logAndEndEvent();
                 return;
             }
         }
@@ -4100,7 +4186,7 @@
 
         synchronized (mLock) {
             // Time passed since Session was created
-            long suggestionSentRelativeTimestamp =
+            final long suggestionSentRelativeTimestamp =
                     SystemClock.elapsedRealtime() - mLatencyBaseTime;
             mPresentationStatsEventLogger.maybeSetSuggestionSentTimestampMs(
                     (int) (suggestionSentRelativeTimestamp));
@@ -4468,10 +4554,19 @@
             saveTriggerId = saveInfo.getTriggerId();
             if (saveTriggerId != null) {
                 writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
+                mSaveEventLogger.maybeSetSaveUiShownReason(SAVE_UI_SHOWN_REASON_TRIGGER_ID_SET);
             }
             flags = saveInfo.getFlags();
             mSaveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
 
+            mFillResponseEventLogger.maybeSetSaveUiTriggerIds(HAVE_SAVE_TRIGGER_ID);
+
+            // Start to log Save event.
+            mSaveEventLogger.maybeSetRequestId(response.getRequestId());
+            mSaveEventLogger.maybeSetAppPackageUid(uid);
+            mSaveEventLogger.maybeSetSaveUiTriggerIds(HAVE_SAVE_TRIGGER_ID);
+            mSaveEventLogger.maybeSetFlag(flags);
+
             // We only need to track views if we want to save once they become invisible.
             if (mSaveOnAllViewsInvisible) {
                 if (trackedViews == null) {
@@ -4479,18 +4574,28 @@
                 }
                 if (saveInfo.getRequiredIds() != null) {
                     Collections.addAll(trackedViews, saveInfo.getRequiredIds());
+                    mSaveEventLogger.maybeSetSaveUiShownReason(
+                        SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE);
                 }
 
                 if (saveInfo.getOptionalIds() != null) {
                     Collections.addAll(trackedViews, saveInfo.getOptionalIds());
+                    mSaveEventLogger.maybeSetSaveUiShownReason(
+                        SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE);
                 }
             }
             if ((flags & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) {
+                mSaveEventLogger.maybeSetSaveUiShownReason(
+                    SAVE_UI_SHOWN_REASON_UNKNOWN);
+                mSaveEventLogger.maybeSetSaveUiNotShownReason(
+                    NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG);
                 saveOnFinish = false;
             }
 
         } else {
             flags = 0;
+            mSaveEventLogger.maybeSetSaveUiNotShownReason(
+                NO_SAVE_REASON_NO_SAVE_INFO);
             saveTriggerId = null;
         }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 1363ef3..ae88f24 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -126,7 +126,7 @@
     private final VirtualDeviceManagerService mService;
     private final PendingTrampolineCallback mPendingTrampolineCallback;
     private final int mOwnerUid;
-    private final int mDeviceId;
+    private int mDeviceId;
     // Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
     // Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
     // 1. After display is created the window manager calls into VDM during construction
@@ -348,6 +348,7 @@
     @Override // Binder call
     public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
             ResultReceiver resultReceiver) {
+        Objects.requireNonNull(pendingIntent);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(displayId)) {
                 throw new SecurityException("Display ID " + displayId
@@ -404,6 +405,7 @@
         super.close_enforcePermission();
         // Remove about-to-be-closed virtual device from the service before butchering it.
         mService.removeVirtualDevice(mDeviceId);
+        mDeviceId = Context.DEVICE_ID_INVALID;
 
         VirtualDisplayWrapper[] virtualDisplaysToBeReleased;
         synchronized (mVirtualDeviceLock) {
@@ -497,6 +499,7 @@
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualDpad_enforcePermission();
+        Objects.requireNonNull(config);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
@@ -517,6 +520,7 @@
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualKeyboard_enforcePermission();
+        Objects.requireNonNull(config);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
@@ -539,6 +543,7 @@
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualMouse_enforcePermission();
+        Objects.requireNonNull(config);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
@@ -560,6 +565,7 @@
     public void createVirtualTouchscreen(VirtualTouchscreenConfig config,
             @NonNull IBinder deviceToken) {
         super.createVirtualTouchscreen_enforcePermission();
+        Objects.requireNonNull(config);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
@@ -590,6 +596,7 @@
     public void createVirtualNavigationTouchpad(VirtualNavigationTouchpadConfig config,
             @NonNull IBinder deviceToken) {
         super.createVirtualNavigationTouchpad_enforcePermission();
+        Objects.requireNonNull(config);
         synchronized (mVirtualDeviceLock) {
             if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 3b1983f..291c0587 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -66,7 +66,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
@@ -86,6 +89,14 @@
     private static AtomicInteger sNextUniqueIndex = new AtomicInteger(
             Context.DEVICE_ID_DEFAULT + 1);
 
+    private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener =
+            new CompanionDeviceManager.OnAssociationsChangedListener() {
+                @Override
+                public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) {
+                    syncVirtualDevicesToCdmAssociations(associations);
+                }
+            };
+
     /**
      * Mapping from device IDs to virtual devices.
      */
@@ -204,11 +215,56 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             getContext().sendBroadcastAsUser(i, UserHandle.ALL);
+
+            synchronized (mVirtualDeviceManagerLock) {
+                if (mVirtualDevices.size() == 0) {
+                    unregisterCdmAssociationListener();
+                }
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
+    private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) {
+        Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
+        synchronized (mVirtualDeviceManagerLock) {
+            if (mVirtualDevices.size() == 0) {
+                return;
+            }
+
+            Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
+            for (AssociationInfo association : associations) {
+                activeAssociationIds.add(association.getId());
+            }
+
+            for (int i = 0; i < mVirtualDevices.size(); i++) {
+                VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
+                if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) {
+                    virtualDevicesToRemove.add(virtualDevice);
+                }
+            }
+        }
+
+        for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
+            virtualDevice.close();
+        }
+
+    }
+
+    private void registerCdmAssociationListener() {
+        final CompanionDeviceManager cdm = getContext().getSystemService(
+                CompanionDeviceManager.class);
+        cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
+                mCdmAssociationListener);
+    }
+
+    private void unregisterCdmAssociationListener() {
+        final CompanionDeviceManager cdm = getContext().getSystemService(
+                CompanionDeviceManager.class);
+        cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
+    }
+
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
 
         private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback =
@@ -254,7 +310,20 @@
             if (associationInfo == null) {
                 throw new IllegalArgumentException("No association with ID " + associationId);
             }
+            Objects.requireNonNull(params);
+            Objects.requireNonNull(activityListener);
+            Objects.requireNonNull(soundEffectListener);
+
             synchronized (mVirtualDeviceManagerLock) {
+                if (mVirtualDevices.size() == 0) {
+                    final long callindId = Binder.clearCallingIdentity();
+                    try {
+                        registerCdmAssociationListener();
+                    } finally {
+                        Binder.restoreCallingIdentity(callindId);
+                    }
+                }
+
                 final UserHandle userHandle = getCallingUserHandle();
                 final CameraAccessController cameraAccessController =
                         getCameraAccessController(userHandle);
@@ -275,6 +344,7 @@
         public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
                 IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)
                 throws RemoteException {
+            Objects.requireNonNull(virtualDisplayConfig);
             final int callingUid = getCallingUid();
             if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
                 throw new SecurityException(
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index af5609a..299f865 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -75,10 +75,11 @@
 
     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
-    private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+    private static final int MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL = 3;
     private static final int MSG_UPDATE_FOREGROUND_APP = 4;
     private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
     private static final int MSG_RUN_UPDATE = 6;
+    private static final int MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL = 7;
 
     // Callbacks for requesting updates to the display's power state
     private final Callbacks mCallbacks;
@@ -216,12 +217,11 @@
     private float mBrightnessAdjustmentSampleOldLux;
     private float mBrightnessAdjustmentSampleOldBrightness;
 
-    // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
-    // user's adjustment) immediately, but wait for a drastic enough change in the ambient light.
-    // The anchor determines what were the light levels when the user has set their preference, and
-    // we use a relative threshold to determine when to revert to the OEM curve.
-    private boolean mShortTermModelValid;
-    private float mShortTermModelAnchor;
+    // The short term models, current and previous. Eg, we might use the "paused" one to save out
+    // the interactive short term model when switching to idle screen brightness mode, and
+    // vice-versa.
+    private final ShortTermModel mShortTermModel;
+    private final ShortTermModel mPausedShortTermModel;
 
     // Controls High Brightness Mode.
     private HighBrightnessModeController mHbmController;
@@ -309,8 +309,8 @@
         mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
         mScreenBrightnessThresholds = screenBrightnessThresholds;
         mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle;
-        mShortTermModelValid = true;
-        mShortTermModelAnchor = -1;
+        mShortTermModel = new ShortTermModel();
+        mPausedShortTermModel = new ShortTermModel();
         mHandler = new AutomaticBrightnessHandler(looper);
         mAmbientLightRingBuffer =
             new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock);
@@ -492,10 +492,10 @@
             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
         }
         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy) && !isInIdleMode()) {
-            mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
+            mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL,
                     mCurrentBrightnessMapper.getShortTermModelTimeout());
         } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
-            mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
+            mHandler.removeMessages(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL);
         }
         return true;
     }
@@ -516,25 +516,13 @@
 
     private boolean setScreenBrightnessByUser(float lux, float brightness) {
         mCurrentBrightnessMapper.addUserDataPoint(lux, brightness);
-        mShortTermModelValid = true;
-        mShortTermModelAnchor = lux;
-        if (mLoggingEnabled) {
-            Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
-        }
+        mShortTermModel.setUserBrightness(lux, brightness);
         return true;
     }
 
     public void resetShortTermModel() {
         mCurrentBrightnessMapper.clearUserDataPoints();
-        mShortTermModelValid = true;
-        mShortTermModelAnchor = -1;
-    }
-
-    private void invalidateShortTermModel() {
-        if (mLoggingEnabled) {
-            Slog.d(TAG, "ShortTermModel: invalidate user data");
-        }
-        mShortTermModelValid = false;
+        mShortTermModel.reset();
     }
 
     public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
@@ -595,8 +583,12 @@
             pw.println("  mShortTermModelTimeout(idle)="
                     + mIdleModeBrightnessMapper.getShortTermModelTimeout());
         }
-        pw.println("  mShortTermModelAnchor=" + mShortTermModelAnchor);
-        pw.println("  mShortTermModelValid=" + mShortTermModelValid);
+        pw.println("  mShortTermModel=");
+        mShortTermModel.dump(pw);
+        pw.println("  mPausedShortTermModel=");
+        mPausedShortTermModel.dump(pw);
+
+        pw.println();
         pw.println("  mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
@@ -740,15 +732,9 @@
         }
         mHbmController.onAmbientLuxChange(mAmbientLux);
 
+
         // If the short term model was invalidated and the change is drastic enough, reset it.
-        if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
-            if (mCurrentBrightnessMapper.shouldResetShortTermModel(
-                    mAmbientLux, mShortTermModelAnchor)) {
-                resetShortTermModel();
-            } else {
-                mShortTermModelValid = true;
-            }
-        }
+        mShortTermModel.maybeReset(mAmbientLux);
     }
 
     private float calculateAmbientLux(long now, long horizon) {
@@ -1118,8 +1104,29 @@
             return;
         }
         Slog.i(TAG, "Switching to Idle Screen Brightness Mode");
+        // Stash short term model
+        ShortTermModel tempShortTermModel = new ShortTermModel();
+        tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(),
+                mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true);
+
+        // Send delayed timeout
+        mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL,
+                mClock.uptimeMillis()
+                        + mCurrentBrightnessMapper.getShortTermModelTimeout());
+
+        Slog.i(TAG, "mPreviousShortTermModel" + mPausedShortTermModel);
+        // new brightness mapper
         mCurrentBrightnessMapper = mIdleModeBrightnessMapper;
-        resetShortTermModel();
+
+        // if previous stm has been invalidated, and lux has drastically changed, just use
+        // the new, reset stm.
+        // if previous stm is still valid then revalidate it
+        if (mPausedShortTermModel != null && !mPausedShortTermModel.maybeReset(mAmbientLux)) {
+            setScreenBrightnessByUser(mPausedShortTermModel.mAnchor,
+                    mPausedShortTermModel.mBrightness);
+        }
+        mPausedShortTermModel.copyFrom(tempShortTermModel);
+
         update();
     }
 
@@ -1128,8 +1135,28 @@
             return;
         }
         Slog.i(TAG, "Switching to Interactive Screen Brightness Mode");
+        ShortTermModel tempShortTermModel = new ShortTermModel();
+        tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(),
+                mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true);
+        mHandler.removeMessages(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL);
+        // Send delayed timeout
+        mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL,
+                mClock.uptimeMillis()
+                        + mCurrentBrightnessMapper.getShortTermModelTimeout());
+        Slog.i(TAG, "mPreviousShortTermModel" + mPausedShortTermModel.toString());
+
+        // restore interactive mapper.
         mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper;
-        resetShortTermModel();
+
+        // if previous stm has been invalidated, and lux has drastically changed, just use
+        // the new, reset stm.
+        // if previous stm is still valid then revalidate it
+        if (!mPausedShortTermModel.maybeReset(mAmbientLux)) {
+            setScreenBrightnessByUser(mPausedShortTermModel.mAnchor,
+                    mPausedShortTermModel.mBrightness);
+        }
+        mPausedShortTermModel.copyFrom(tempShortTermModel);
+
         update();
     }
 
@@ -1164,6 +1191,77 @@
         }
     }
 
+    private class ShortTermModel {
+        // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
+        // user's adjustment) immediately, but wait for a drastic enough change in the ambient
+        // light.
+        // The anchor determines what were the light levels when the user has set their preference,
+        // and we use a relative threshold to determine when to revert to the OEM curve.
+        private float mAnchor = -1f;
+        private float mBrightness;
+        private boolean mIsValid = true;
+
+        private void reset() {
+            mAnchor = -1f;
+            mBrightness = -1f;
+            mIsValid = true;
+        }
+
+        private void invalidate() {
+            mIsValid = false;
+            if (mLoggingEnabled) {
+                Slog.d(TAG, "ShortTermModel: invalidate user data");
+            }
+        }
+
+        private void setUserBrightness(float lux, float brightness) {
+            mAnchor = lux;
+            mBrightness = brightness;
+            mIsValid = true;
+            if (mLoggingEnabled) {
+                Slog.d(TAG, "ShortTermModel: anchor=" + mAnchor);
+            }
+        }
+
+        private boolean maybeReset(float currentLux) {
+            // If the short term model was invalidated and the change is drastic enough, reset it.
+            // Otherwise, we revalidate it.
+            if (!mIsValid && mAnchor != -1) {
+                if (mCurrentBrightnessMapper != null
+                        && mCurrentBrightnessMapper.shouldResetShortTermModel(
+                        currentLux, mAnchor)) {
+                    resetShortTermModel();
+                } else {
+                    mIsValid = true;
+                }
+                return mIsValid;
+            }
+            return false;
+        }
+
+        private void set(float anchor, float brightness, boolean valid) {
+            mAnchor = anchor;
+            mBrightness = brightness;
+            mIsValid = valid;
+        }
+        private void copyFrom(ShortTermModel from) {
+            mAnchor = from.mAnchor;
+            mBrightness = from.mBrightness;
+            mIsValid = from.mIsValid;
+        }
+
+        public String toString() {
+            return " mAnchor: " + mAnchor
+                    + "\n mBrightness: " + mBrightness
+                    + "\n mIsValid: " + mIsValid;
+        }
+
+        void dump(PrintWriter pw) {
+            pw.println(this);
+        }
+
+    }
+
     private final class AutomaticBrightnessHandler extends Handler {
         public AutomaticBrightnessHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -1184,8 +1282,8 @@
                     collectBrightnessAdjustmentSample();
                     break;
 
-                case MSG_INVALIDATE_SHORT_TERM_MODEL:
-                    invalidateShortTermModel();
+                case MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL:
+                    mShortTermModel.invalidate();
                     break;
 
                 case MSG_UPDATE_FOREGROUND_APP:
@@ -1195,6 +1293,10 @@
                 case MSG_UPDATE_FOREGROUND_APP_SYNC:
                     updateForegroundAppSync();
                     break;
+
+                case MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL:
+                    mPausedShortTermModel.invalidate();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index d047183..3456e3e 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -363,13 +363,17 @@
     public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment);
 
     /**
-     * Returns the timeout for the short term model
+     * Returns the timeout, in milliseconds for the short term model
      *
      * Timeout after which we remove the effects any user interactions might've had on the
      * brightness mapping. This timeout doesn't start until we transition to a non-interactive
      * display policy so that we don't reset while users are using their devices, but also so that
      * we don't erroneously keep the short-term model if the device is dozing but the
      * display is fully on.
+     *
+     * This timeout is also used when the device switches from interactive screen brightness mode
+     * to idle screen brightness mode, to preserve the user's preference when they resume usage of
+     * the device, within the specified timeframe.
      */
     public abstract long getShortTermModelTimeout();
 
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index eccee52..067a2d6 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -16,7 +16,10 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
@@ -63,8 +66,16 @@
     private final DeviceConfigInterface mDeviceConfig;
 
     private int mThrottlingStatus;
+
+    // Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
+    @NonNull
+    private HashMap<String, BrightnessThrottlingData> mDdcThrottlingDataMap;
+
+    // Current throttling data being used.
+    // Null if we do not support throttling.
+    @Nullable
     private BrightnessThrottlingData mThrottlingData;
-    private BrightnessThrottlingData mDdcThrottlingData;
+
     private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
     private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
         BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -73,35 +84,45 @@
     // The most recent string that has been set from DeviceConfig
     private String mBrightnessThrottlingDataString;
 
+    // The brightness throttling configuration that should be used.
+    private String mBrightnessThrottlingDataId;
+
     // This is a collection of brightness throttling data that has been written as overrides from
     // the DeviceConfig. This will always take priority over the display device config data.
-    private HashMap<String, BrightnessThrottlingData> mBrightnessThrottlingDataOverride =
-            new HashMap<>(1);
+    // We need to store the data for every display device, so we do not need to update this each
+    // time the underlying display device changes.
+    // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData.
+    // HashMap< uniqueDisplayId, HashMap< throttlingDataId, BrightnessThrottlingData >>
+    private final HashMap<String, HashMap<String, BrightnessThrottlingData>>
+            mBrightnessThrottlingDataOverride = new HashMap<>(1);
 
-    BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData,
-            Runnable throttlingChangeCallback, String uniqueDisplayId) {
-        this(new Injector(), handler, handler, throttlingData, throttlingChangeCallback,
-                uniqueDisplayId);
+    BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
+            String throttlingDataId,
+            @NonNull HashMap<String, BrightnessThrottlingData> brightnessThrottlingDataMap) {
+        this(new Injector(), handler, handler, throttlingChangeCallback,
+                uniqueDisplayId, throttlingDataId, brightnessThrottlingDataMap);
     }
 
     @VisibleForTesting
     BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
-            BrightnessThrottlingData throttlingData, Runnable throttlingChangeCallback,
-            String uniqueDisplayId) {
+            Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
+            @NonNull HashMap<String, BrightnessThrottlingData> brightnessThrottlingDataMap) {
         mInjector = injector;
 
         mHandler = handler;
         mDeviceConfigHandler = deviceConfigHandler;
-        mThrottlingData = throttlingData;
-        mDdcThrottlingData = throttlingData;
+        mDdcThrottlingDataMap = brightnessThrottlingDataMap;
         mThrottlingChangeCallback = throttlingChangeCallback;
         mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
 
         mUniqueDisplayId = uniqueDisplayId;
         mDeviceConfig = injector.getDeviceConfig();
         mDeviceConfigListener = new DeviceConfigListener();
-
-        resetThrottlingData(mThrottlingData, mUniqueDisplayId);
+        mBrightnessThrottlingDataId = throttlingDataId;
+        mDdcThrottlingDataMap = brightnessThrottlingDataMap;
+        loadBrightnessThrottlingDataFromDeviceConfig();
+        loadBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThrottlingDataMap,
+                mBrightnessThrottlingDataId, mUniqueDisplayId);
     }
 
     boolean deviceSupportsThrottling() {
@@ -133,23 +154,14 @@
         mThrottlingStatus = THROTTLING_INVALID;
     }
 
-    private void resetThrottlingData() {
-        resetThrottlingData(mDdcThrottlingData, mUniqueDisplayId);
-    }
-
-    void resetThrottlingData(BrightnessThrottlingData throttlingData, String displayId) {
-        stop();
-
-        mUniqueDisplayId = displayId;
-        mDdcThrottlingData = throttlingData;
-        mDeviceConfigListener.startListening();
-        reloadBrightnessThrottlingDataOverride();
-        mThrottlingData = mBrightnessThrottlingDataOverride.getOrDefault(mUniqueDisplayId,
-                throttlingData);
-
-        if (deviceSupportsThrottling()) {
-            mSkinThermalStatusObserver.startObserving();
-        }
+    void loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+            HashMap<String, BrightnessThrottlingData> ddcThrottlingDataMap,
+            String brightnessThrottlingDataId,
+            String uniqueDisplayId) {
+        mDdcThrottlingDataMap = ddcThrottlingDataMap;
+        mBrightnessThrottlingDataId = brightnessThrottlingDataId;
+        mUniqueDisplayId = uniqueDisplayId;
+        resetThrottlingData();
     }
 
     private float verifyAndConstrainBrightnessCap(float brightness) {
@@ -183,7 +195,7 @@
         float brightnessCap = PowerManager.BRIGHTNESS_MAX;
         int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
 
-        if (mThrottlingStatus != THROTTLING_INVALID) {
+        if (mThrottlingStatus != THROTTLING_INVALID && mThrottlingData != null) {
             // Throttling levels are sorted by increasing severity
             for (ThrottlingLevel level : mThrottlingData.throttlingLevels) {
                 if (level.thermalStatus <= mThrottlingStatus) {
@@ -218,13 +230,14 @@
 
     private void dumpLocal(PrintWriter pw) {
         pw.println("BrightnessThrottler:");
+        pw.println("  mBrightnessThrottlingDataId=" + mBrightnessThrottlingDataId);
         pw.println("  mThrottlingData=" + mThrottlingData);
-        pw.println("  mDdcThrottlingData=" + mDdcThrottlingData);
         pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
         pw.println("  mThrottlingStatus=" + mThrottlingStatus);
         pw.println("  mBrightnessCap=" + mBrightnessCap);
         pw.println("  mBrightnessMaxReason=" +
             BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+        pw.println("  mDdcThrottlingDataMap=" + mDdcThrottlingDataMap);
         pw.println("  mBrightnessThrottlingDataOverride=" + mBrightnessThrottlingDataOverride);
         pw.println("  mBrightnessThrottlingDataString=" + mBrightnessThrottlingDataString);
 
@@ -237,8 +250,18 @@
                 /* defaultValue= */ null);
     }
 
-    private boolean parseAndSaveData(@NonNull String strArray,
-            @NonNull HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData) {
+    // The brightness throttling data id may or may not be specified in the string that is passed
+    // in, if there is none specified, we assume it is for the default case. Each string passed in
+    // here must be for one display and one throttling id.
+    // 123,1,critical,0.8
+    // 456,2,moderate,0.9,critical,0.7
+    // 456,2,moderate,0.9,critical,0.7,default
+    // 456,2,moderate,0.9,critical,0.7,id_2
+    // displayId, number, <state, val> * number
+    // displayId, <number, <state, val> * number>, throttlingId
+    private boolean parseAndAddData(@NonNull String strArray,
+            @NonNull HashMap<String, HashMap<String, BrightnessThrottlingData>>
+                    displayIdToThrottlingIdToBtd) {
         boolean validConfig = true;
         String[] items = strArray.split(",");
         int i = 0;
@@ -254,29 +277,42 @@
             for (int j = 0; j < noOfThrottlingPoints; j++) {
                 String severity = items[i++];
                 int status = parseThermalStatus(severity);
-
                 float brightnessPoint = parseBrightness(items[i++]);
-
                 throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint));
             }
-            BrightnessThrottlingData toSave =
+
+            String throttlingDataId = (i < items.length) ? items[i++] : DEFAULT_ID;
+            BrightnessThrottlingData throttlingLevelsData =
                     DisplayDeviceConfig.BrightnessThrottlingData.create(throttlingLevels);
-            tempBrightnessThrottlingData.put(uniqueDisplayId, toSave);
+
+            // Add throttlingLevelsData to inner map where necessary.
+            HashMap<String, BrightnessThrottlingData> throttlingMapForDisplay =
+                    displayIdToThrottlingIdToBtd.get(uniqueDisplayId);
+            if (throttlingMapForDisplay == null) {
+                throttlingMapForDisplay = new HashMap<>();
+                throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData);
+                displayIdToThrottlingIdToBtd.put(uniqueDisplayId, throttlingMapForDisplay);
+            } else if (throttlingMapForDisplay.containsKey(throttlingDataId)) {
+                Slog.e(TAG, "Throttling data for display " + uniqueDisplayId
+                        + "contains duplicate throttling ids: '" + throttlingDataId + "'");
+                return false;
+            } else {
+                throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData);
+            }
         } catch (NumberFormatException | IndexOutOfBoundsException
                 | UnknownThermalStatusException e) {
-            validConfig = false;
             Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e);
+            validConfig = false;
         }
 
         if (i != items.length) {
             validConfig = false;
         }
-
         return validConfig;
     }
 
-    public void reloadBrightnessThrottlingDataOverride() {
-        HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData =
+    private void loadBrightnessThrottlingDataFromDeviceConfig() {
+        HashMap<String, HashMap<String, BrightnessThrottlingData>> tempThrottlingData =
                 new HashMap<>(1);
         mBrightnessThrottlingDataString = getBrightnessThrottlingDataString();
         boolean validConfig = true;
@@ -284,15 +320,15 @@
         if (mBrightnessThrottlingDataString != null) {
             String[] throttlingDataSplits = mBrightnessThrottlingDataString.split(";");
             for (String s : throttlingDataSplits) {
-                if (!parseAndSaveData(s, tempBrightnessThrottlingData)) {
+                if (!parseAndAddData(s, tempThrottlingData)) {
                     validConfig = false;
                     break;
                 }
             }
 
             if (validConfig) {
-                mBrightnessThrottlingDataOverride.putAll(tempBrightnessThrottlingData);
-                tempBrightnessThrottlingData.clear();
+                mBrightnessThrottlingDataOverride.putAll(tempThrottlingData);
+                tempThrottlingData.clear();
             }
 
         } else {
@@ -300,15 +336,50 @@
         }
     }
 
+    private void resetThrottlingData() {
+        stop();
+
+        mDeviceConfigListener.startListening();
+
+        // Get throttling data for this id, if it exists
+        mThrottlingData = getConfigFromId(mBrightnessThrottlingDataId);
+
+        // Fallback to default id otherwise.
+        if (!DEFAULT_ID.equals(mBrightnessThrottlingDataId) && mThrottlingData == null) {
+            mThrottlingData = getConfigFromId(DEFAULT_ID);
+            Slog.d(TAG, "Falling back to default throttling Id");
+        }
+
+        if (deviceSupportsThrottling()) {
+            mSkinThermalStatusObserver.startObserving();
+        }
+    }
+
+    private BrightnessThrottlingData getConfigFromId(String id) {
+        BrightnessThrottlingData returnValue;
+
+        // Fallback pattern for fetching correct throttling data for this display and id.
+        // 1) throttling data from device config for this throttling data id
+        returnValue =  mBrightnessThrottlingDataOverride.get(mUniqueDisplayId) == null
+                ? null
+                : mBrightnessThrottlingDataOverride.get(mUniqueDisplayId).get(id);
+        // 2) throttling data from ddc for this throttling data id
+        returnValue = returnValue == null
+                ? mDdcThrottlingDataMap.get(id)
+                : returnValue;
+
+        return returnValue;
+    }
+
     /**
      * Listens to config data change and updates the brightness throttling data using
      * DisplayManager#KEY_BRIGHTNESS_THROTTLING_DATA.
      * The format should be a string similar to: "local:4619827677550801152,2,moderate,0.5,severe,
      * 0.379518072;local:4619827677550801151,1,moderate,0.75"
      * In this order:
-     * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>]
-     * Where the latter part is repeated for each throttling level, and the entirety is repeated
-     * for each display, separated by a semicolon.
+     * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>][,throttlingId]?
+     * Where [<severity as string>,<brightness cap>] is repeated for each throttling level, and the
+     * entirety is repeated for each display & throttling data id, separated by a semicolon.
      */
     public class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
         public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
@@ -320,7 +391,7 @@
 
         @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
-            reloadBrightnessThrottlingDataOverride();
+            loadBrightnessThrottlingDataFromDeviceConfig();
             resetThrottlingData();
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index f4b3f1a..da02115 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -143,17 +143,17 @@
  *            <brightness>0.01</brightness>
  *          </brightnessThrottlingPoint>
  *        </brightnessThrottlingMap>
- *        <concurrentDisplaysBrightnessThrottlingMap>
- *          <brightnessThrottlingPoint>
- *            <thermalStatus>severe</thermalStatus>
- *            <brightness>0.07</brightness>
- *          </brightnessThrottlingPoint>
- *          <brightnessThrottlingPoint>
- *            <thermalStatus>critical</thermalStatus>
- *            <brightness>0.005</brightness>
- *          </brightnessThrottlingPoint>
- *        </concurrentDisplaysBrightnessThrottlingMap>
- *        <refreshRateThrottlingMap>
+ *        <brightnessThrottlingMap id="id_2"> // optional attribute, leave blank for default
+ *             <brightnessThrottlingPoint>
+ *                 <thermalStatus>moderate</thermalStatus>
+ *                 <brightness>0.2</brightness>
+ *             </brightnessThrottlingPoint>
+ *             <brightnessThrottlingPoint>
+ *                 <thermalStatus>severe</thermalStatus>
+ *                 <brightness>0.1</brightness>
+ *            </brightnessThrottlingPoint>
+ *        </brightnessThrottlingMap>
+         <refreshRateThrottlingMap>
  *            <refreshRateThrottlingPoint>
  *                <thermalStatus>critical</thermalStatus>
  *                <refreshRateRange>
@@ -687,8 +687,8 @@
     private int[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
     private int[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
 
-    private final Map<String, BrightnessThrottlingData> mBrightnessThrottlingDataMap =
-            new HashMap<>();
+    private final HashMap<String, BrightnessThrottlingData>
+            mBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
 
     private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
             mRefreshRateThrottlingMap = new HashMap<>();
@@ -1346,11 +1346,11 @@
     }
 
     /**
-     * @param id The ID of the throttling data
-     * @return brightness throttling configuration data for the display.
+     * @return brightness throttling configuration data for this display, for each throttling id.
      */
-    public BrightnessThrottlingData getBrightnessThrottlingData(String id) {
-        return BrightnessThrottlingData.create(mBrightnessThrottlingDataMap.get(id));
+    public HashMap<String, BrightnessThrottlingData>
+            getBrightnessThrottlingDataMapByThrottlingId() {
+        return mBrightnessThrottlingDataMapByThrottlingId;
     }
 
     /**
@@ -1525,7 +1525,8 @@
                 + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
                 + ", mHbmData=" + mHbmData
                 + ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
-                + ", mBrightnessThrottlingData=" + mBrightnessThrottlingDataMap
+                + ", mBrightnessThrottlingDataMapByThrottlingId="
+                + mBrightnessThrottlingDataMapByThrottlingId
                 + "\n"
                 + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
                 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
@@ -1918,11 +1919,11 @@
             if (!badConfig) {
                 String id = map.getId() == null ? DEFAULT_ID
                         : map.getId();
-                if (mBrightnessThrottlingDataMap.containsKey(id)) {
+                if (mBrightnessThrottlingDataMapByThrottlingId.containsKey(id)) {
                     throw new RuntimeException("Brightness throttling data with ID " + id
                             + " already exists");
                 }
-                mBrightnessThrottlingDataMap.put(id,
+                mBrightnessThrottlingDataMapByThrottlingId.put(id,
                         BrightnessThrottlingData.create(throttlingLevels));
             }
         }
@@ -1971,8 +1972,8 @@
                 ));
             }
             if (refreshRates.size() == 0) {
-                Slog.w(TAG, "RefreshRateThrottling: no valid throttling points fond for map, mapId="
-                        + id);
+                Slog.w(TAG, "RefreshRateThrottling: no valid throttling points found for map, "
+                        + "mapId=" + id);
                 continue;
             }
             mRefreshRateThrottlingMap.put(id, refreshRates);
@@ -3077,7 +3078,7 @@
 
 
         /**
-         * Creates multiple teperature based throttling levels of brightness
+         * Creates multiple temperature based throttling levels of brightness
          */
         public static BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels) {
             if (throttlingLevels == null || throttlingLevels.size() == 0) {
@@ -3120,15 +3121,6 @@
             return new BrightnessThrottlingData(throttlingLevels);
         }
 
-        static public BrightnessThrottlingData create(BrightnessThrottlingData other) {
-            if (other == null) {
-                return null;
-            }
-
-            return BrightnessThrottlingData.create(other.throttlingLevels);
-        }
-
-
         @Override
         public String toString() {
             return "BrightnessThrottlingData{"
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f5859ee..2322e03 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -505,7 +505,7 @@
 
     private DisplayDeviceConfig mDisplayDeviceConfig;
 
-    // Identifiers for suspend blocker acuisition requests
+    // Identifiers for suspend blocker acquisition requests
     private final String mSuspendBlockerIdUnfinishedBusiness;
     private final String mSuspendBlockerIdOnStateChanged;
     private final String mSuspendBlockerIdProxPositive;
@@ -515,6 +515,7 @@
     private boolean mIsEnabled;
     private boolean mIsInTransition;
 
+    // The id of the brightness throttling policy that should be used.
     private String mBrightnessThrottlingDataId;
 
     // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
@@ -905,14 +906,15 @@
                 loadNitBasedBrightnessSetting();
 
                 /// Since the underlying display-device changed, we really don't know the
-                // last command that was sent to change it's state. Lets assume it is unknown so
+                // last command that was sent to change it's state. Let's assume it is unknown so
                 // that we trigger a change immediately.
                 mPowerState.resetScreenState();
             } else if (!mBrightnessThrottlingDataId.equals(brightnessThrottlingDataId)) {
                 changed = true;
                 mBrightnessThrottlingDataId = brightnessThrottlingDataId;
-                mBrightnessThrottler.resetThrottlingData(
-                        config.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
+                mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+                        config.getBrightnessThrottlingDataMapByThrottlingId(),
+                        mBrightnessThrottlingDataId,
                         mUniqueDisplayId);
             }
             if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
@@ -981,9 +983,9 @@
                                 sdrBrightness, maxDesiredHdrSdrRatio);
                     }
                 });
-        mBrightnessThrottler.resetThrottlingData(
-                mDisplayDeviceConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
-                mUniqueDisplayId);
+        mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+                mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId(),
+                mBrightnessThrottlingDataId, mUniqueDisplayId);
     }
 
     private void sendUpdatePowerState() {
@@ -2116,11 +2118,11 @@
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
         return new BrightnessThrottler(mHandler,
-                ddConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
                 () -> {
                     sendUpdatePowerState();
                     postBrightnessChangeRunnable();
-                }, mUniqueDisplayId);
+                }, mUniqueDisplayId, mLogicalDisplay.getBrightnessThrottlingDataIdLocked(),
+                ddConfig.getBrightnessThrottlingDataMapByThrottlingId());
     }
 
     private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 8ce4b66..d2af88b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -400,6 +400,7 @@
     private boolean mIsEnabled;
     private boolean mIsInTransition;
 
+    // The id of the brightness throttling policy that should be used.
     private String mBrightnessThrottlingDataId;
 
     // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
@@ -722,14 +723,15 @@
                 mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
 
                 // Since the underlying display-device changed, we really don't know the
-                // last command that was sent to change it's state. Lets assume it is unknown so
+                // last command that was sent to change it's state. Let's assume it is unknown so
                 // that we trigger a change immediately.
                 mPowerState.resetScreenState();
             } else if (!mBrightnessThrottlingDataId.equals(brightnessThrottlingDataId)) {
                 changed = true;
                 mBrightnessThrottlingDataId = brightnessThrottlingDataId;
-                mBrightnessThrottler.resetThrottlingData(
-                        config.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
+                mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+                        config.getBrightnessThrottlingDataMapByThrottlingId(),
+                        mBrightnessThrottlingDataId,
                         mUniqueDisplayId);
             }
             if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
@@ -795,9 +797,9 @@
                                 sdrBrightness, maxDesiredHdrSdrRatio);
                     }
                 });
-        mBrightnessThrottler.resetThrottlingData(
-                mDisplayDeviceConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
-                mUniqueDisplayId);
+        mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+                mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId(),
+                mBrightnessThrottlingDataId, mUniqueDisplayId);
     }
 
     private void sendUpdatePowerState() {
@@ -1757,11 +1759,11 @@
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
         return new BrightnessThrottler(mHandler,
-                ddConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
                 () -> {
                     sendUpdatePowerState();
                     postBrightnessChangeRunnable();
-                }, mUniqueDisplayId);
+                }, mUniqueDisplayId, mLogicalDisplay.getBrightnessThrottlingDataIdLocked(),
+                ddConfig.getBrightnessThrottlingDataMapByThrottlingId());
     }
 
     private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
index f86ee24..634f31d 100644
--- a/services/core/java/com/android/server/display/layout/Layout.java
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -338,7 +338,9 @@
         }
 
         /**
-         * @return The ID of the brightness throttling map that this display should use.
+         * Gets the id of the brightness throttling map that should be used.
+         * @return The ID of the brightness throttling map that this display should use, null if
+         *         unspecified, will fall back to default.
          */
         public String getBrightnessThrottlingMapId() {
             return mBrightnessThrottlingMapId;
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index bb1a445..effef47 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -37,6 +37,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
@@ -273,6 +274,9 @@
         }
         mHandwritingSurface.startIntercepting(imePid, imeUid);
 
+        // Unset the pointer icon for the stylus in case the app had set it.
+        InputManagerGlobal.getInstance().setPointerIconType(PointerIcon.TYPE_NOT_SPECIFIED);
+
         return new HandwritingSession(mCurrentRequestId, mHandwritingSurface.getInputChannel(),
                 mHandwritingBuffer);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 81cca50..8707059 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6625,20 +6625,15 @@
                             || r.getImportance() == IMPORTANCE_NONE)) {
                 // Increase the importance of foreground service notifications unless the user had
                 // an opinion otherwise (and the channel hasn't yet shown a fg service).
-                if (TextUtils.isEmpty(channelId)
-                        || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
-                    r.setSystemImportance(IMPORTANCE_LOW);
-                } else {
-                    channel.setImportance(IMPORTANCE_LOW);
-                    r.setSystemImportance(IMPORTANCE_LOW);
-                    if (!fgServiceShown) {
-                        channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-                        channel.setFgServiceShown(true);
-                    }
-                    mPreferencesHelper.updateNotificationChannel(
-                            pkg, notificationUid, channel, false);
-                    r.updateNotificationChannel(channel);
+                channel.setImportance(IMPORTANCE_LOW);
+                r.setSystemImportance(IMPORTANCE_LOW);
+                if (!fgServiceShown) {
+                    channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+                    channel.setFgServiceShown(true);
                 }
+                mPreferencesHelper.updateNotificationChannel(
+                        pkg, notificationUid, channel, false);
+                r.updateNotificationChannel(channel);
             } else if (!fgServiceShown && !TextUtils.isEmpty(channelId)
                     && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
                 channel.setFgServiceShown(true);
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
index 2d3ede0..f586126 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
@@ -70,12 +70,14 @@
                 Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
                 wrapCallback(mCallback::onCancel);
             } else if (e instanceof RkpProxyException) {
-                Log.e(TAG, "RKP error fetching key for client " + mCallback.hashCode(), e);
+                Log.e(TAG, "RKP error fetching key for client " + mCallback.hashCode() + ": "
+                        + e.getMessage());
                 RkpProxyException rkpException = (RkpProxyException) e;
                 wrapCallback(() -> mCallback.onError(toGetKeyError(rkpException),
                         e.getMessage()));
             } else {
-                Log.e(TAG, "Error fetching key for client " + mCallback.hashCode(), e);
+                Log.e(TAG, "Unknown error fetching key for client " + mCallback.hashCode() + ": "
+                        + e.getMessage());
                 wrapCallback(() -> mCallback.onError(IGetKeyCallback.ErrorCode.ERROR_UNKNOWN,
                         e.getMessage()));
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5f56923..8346e7c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4068,7 +4068,7 @@
     }
 
     void finishRelaunching() {
-        mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(false);
+        mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(false);
         mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
 
         if (mPendingRelaunchCount > 0) {
@@ -9500,7 +9500,7 @@
                 mRelaunchReason = RELAUNCH_REASON_NONE;
             }
             if (isRequestedOrientationChanged) {
-                mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(true);
+                mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(true);
             }
             if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 6773bcd..e447049 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -487,7 +487,7 @@
                 // The verdict changed from allow (resultIfPiSenderAllowsBal) to block, PI sender
                 // default change is on (otherwise we would have fallen into if above) and we'd
                 // allow if it were off
-                Slog.wtf(TAG, "Without BAL hardening this activity start would NOT be allowed!"
+                Slog.wtf(TAG, "Without BAL hardening this activity start would be allowed!"
                         + stateDumpLog);
             }
         }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index b0c384d..93233dd 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -50,6 +50,7 @@
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
@@ -235,9 +236,14 @@
     private final Boolean mBooleanPropertyIgnoreRequestedOrientation;
 
     @Nullable
+    private final Boolean mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected;
+
+    @Nullable
     private final Boolean mBooleanPropertyFakeFocus;
 
-    private boolean mIsRelauchingAfterRequestedOrientationChanged;
+    private boolean mIsRelaunchingAfterRequestedOrientationChanged;
+
+    private boolean mLastShouldShowLetterboxUi;
 
     private boolean mDoubleTapEvent;
 
@@ -253,6 +259,10 @@
                 readComponentProperty(packageManager, mActivityRecord.packageName,
                         mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled,
                         PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+        mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected =
+                readComponentProperty(packageManager, mActivityRecord.packageName,
+                        mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled,
+                        PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
         mBooleanPropertyFakeFocus =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
                         mLetterboxConfiguration::isCompatFakeFocusEnabled,
@@ -387,7 +397,7 @@
                         ::isPolicyForIgnoringRequestedOrientationEnabled,
                 mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled,
                 mBooleanPropertyIgnoreRequestedOrientation)) {
-            if (mIsRelauchingAfterRequestedOrientationChanged) {
+            if (mIsRelaunchingAfterRequestedOrientationChanged) {
                 Slog.w(TAG, "Ignoring orientation update to "
                         + screenOrientationToString(requestedOrientation)
                         + " due to relaunching after setRequestedOrientation for "
@@ -422,6 +432,8 @@
      *
      * <p>This treatment is enabled when the following conditions are met:
      * <ul>
+     *     <li>Flag gating the treatment is enabled
+     *     <li>Opt-out component property isn't enabled
      *     <li>Per-app override is enabled
      *     <li>App has requested orientation more than 2 times within 1-second
      *     timer and activity is not letterboxed for fixed orientation
@@ -429,7 +441,11 @@
      */
     @VisibleForTesting
     boolean shouldIgnoreOrientationRequestLoop() {
-        if (!mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled) {
+        if (!shouldEnableWithOptInOverrideAndOptOutProperty(
+                /* gatingCondition */ mLetterboxConfiguration
+                    ::isPolicyForIgnoringRequestedOrientationEnabled,
+                mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled,
+                mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected)) {
             return false;
         }
 
@@ -476,8 +492,8 @@
      * Sets whether an activity is relaunching after the app has called {@link
      * android.app.Activity#setRequestedOrientation}.
      */
-    void setRelauchingAfterRequestedOrientationChanged(boolean isRelaunching) {
-        mIsRelauchingAfterRequestedOrientationChanged = isRelaunching;
+    void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) {
+        mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching;
     }
 
     /**
@@ -1154,12 +1170,28 @@
 
     @VisibleForTesting
     boolean shouldShowLetterboxUi(WindowState mainWindow) {
-        return (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow))
+        if (mIsRelaunchingAfterRequestedOrientationChanged || !isSurfaceReadyToShow(mainWindow)) {
+            return mLastShouldShowLetterboxUi;
+        }
+
+        final boolean shouldShowLetterboxUi =
+                (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow))
                 && mainWindow.areAppWindowBoundsLetterboxed()
                 // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
                 // WindowContainer#showWallpaper because the later will return true when this
                 // activity is using blurred wallpaper for letterbox background.
                 && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
+
+        mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
+
+        return shouldShowLetterboxUi;
+    }
+
+    @VisibleForTesting
+    boolean isSurfaceReadyToShow(WindowState mainWindow) {
+        return mainWindow.isDrawn() // Regular case
+                // Waiting for relayoutWindow to call preserveSurface
+                || mainWindow.isDragResizeChanged();
     }
 
     @VisibleForTesting
@@ -1297,6 +1329,10 @@
         return null;
     }
 
+    boolean getIsRelaunchingAfterRequestedOrientationChanged() {
+        return mIsRelaunchingAfterRequestedOrientationChanged;
+    }
+
     private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
         // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
         // an insets frame is equal to a navigation bar which shouldn't affect position of
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 23934e0..736f489 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5230,8 +5230,17 @@
             if (surfaceInsetsChanged) {
                 mLastSurfaceInsets.set(mAttrs.surfaceInsets);
             }
-            if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation()
-                    && okToDisplay() && mSyncState == SYNC_STATE_NONE) {
+            final boolean surfaceResizedWithoutMoveAnimation = surfaceSizeChanged
+                    && mWinAnimator.getShown() && !canPlayMoveAnimation() && okToDisplay()
+                    && mSyncState == SYNC_STATE_NONE;
+            final ActivityRecord activityRecord = getActivityRecord();
+            // If this window belongs to an activity that is relaunching due to an orientation
+            // change then delay the position update until it has redrawn to avoid any flickers.
+            final boolean isLetterboxedAndRelaunching = activityRecord != null
+                    && activityRecord.areBoundsLetterboxed()
+                    && activityRecord.mLetterboxUiController
+                        .getIsRelaunchingAfterRequestedOrientationChanged();
+            if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) {
                 applyWithNextDraw(mSetSurfacePositionConsumer);
             } else {
                 mSetSurfacePositionConsumer.accept(t);
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index dce7b87..bbebbf2 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -40,11 +40,13 @@
         implements ProviderSession.ProviderInternalCallback<Void> {
     private static final String TAG = "GetRequestSession";
 
-    public ClearRequestSession(Context context, int userId, int callingUid,
+    public ClearRequestSession(Context context, RequestSession.SessionLifetime sessionCallback,
+            Object lock, int userId, int callingUid,
             IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
             CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
             long startedTimestamp) {
-        super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED,
+        super(context, sessionCallback, lock, userId, callingUid, request, callback,
+                RequestInfo.TYPE_UNDEFINED,
                 callingAppInfo, cancellationSignal, startedTimestamp);
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 98dc8ab..4c456a8 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -49,13 +49,15 @@
         implements ProviderSession.ProviderInternalCallback<CreateCredentialResponse> {
     private static final String TAG = "CreateRequestSession";
 
-    CreateRequestSession(@NonNull Context context, int userId, int callingUid,
+    CreateRequestSession(@NonNull Context context, RequestSession.SessionLifetime sessionCallback,
+            Object lock, int userId, int callingUid,
             CreateCredentialRequest request,
             ICreateCredentialCallback callback,
             CallingAppInfo callingAppInfo,
             CancellationSignal cancellationSignal,
             long startedTimestamp) {
-        super(context, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE,
+        super(context, sessionCallback, lock, userId, callingUid, request, callback,
+                RequestInfo.TYPE_CREATE,
                 callingAppInfo, cancellationSignal, startedTimestamp);
     }
 
@@ -83,6 +85,7 @@
     @Override
     protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
         mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
+        mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
         try {
             mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
                     RequestInfo.newCreateRequestInfo(
@@ -93,6 +96,7 @@
                     providerDataList));
         } catch (RemoteException e) {
             mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
+            mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
             respondToClientWithErrorAndFinish(
                     CreateCredentialException.TYPE_UNKNOWN,
                     "Unable to invoke selector");
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index de06d44..ae6eaf0 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -33,7 +33,6 @@
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialException;
 import android.credentials.CreateCredentialRequest;
-import android.credentials.CredentialManager;
 import android.credentials.CredentialOption;
 import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
@@ -50,6 +49,7 @@
 import android.credentials.ui.IntentFactory;
 import android.os.Binder;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -70,9 +70,11 @@
 import com.android.server.infra.SecureSettingsServiceNameResolver;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -102,6 +104,13 @@
     private final SparseArray<List<CredentialManagerServiceImpl>> mSystemServicesCacheList =
             new SparseArray<>();
 
+    /** Cache of all ongoing request sessions per user id. */
+    @GuardedBy("mLock")
+    private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions =
+            new SparseArray<>();
+
+    private final SessionManager mSessionManager = new SessionManager();
+
     public CredentialManagerService(@NonNull Context context) {
         super(
                 context,
@@ -331,7 +340,7 @@
 
     @NonNull
     private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
-            getFilteredResultFromRegistry(List<CredentialOption> options) {
+    getFilteredResultFromRegistry(List<CredentialOption> options) {
         // Session for active/provisioned credential descriptions;
         CredentialDescriptionRegistry registry =
                 CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
@@ -389,14 +398,6 @@
         return providerSessions;
     }
 
-    private List<CredentialProviderInfo> getServicesForCredentialDescription(int userId) {
-        return CredentialProviderInfoFactory.getCredentialProviderServices(
-                mContext,
-                userId,
-                CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS,
-                new HashSet<>());
-    }
-
     @Override
     @GuardedBy("CredentialDescriptionRegistry.sLock")
     public void onUserStopped(@NonNull TargetUser user) {
@@ -448,6 +449,8 @@
             final GetRequestSession session =
                     new GetRequestSession(
                             getContext(),
+                            mSessionManager,
+                            mLock,
                             userId,
                             callingUid,
                             callback,
@@ -455,6 +458,7 @@
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
                             CancellationSignal.fromTransport(cancelTransport),
                             timestampBegan);
+            addSessionLocked(userId, session);
 
             List<ProviderSession> providerSessions =
                     prepareProviderSessions(request, session);
@@ -499,6 +503,8 @@
             final PrepareGetRequestSession session =
                     new PrepareGetRequestSession(
                             getContext(),
+                            mSessionManager,
+                            mLock,
                             userId,
                             callingUid,
                             getCredentialCallback,
@@ -515,8 +521,8 @@
                     // TODO: fix
                     prepareGetCredentialCallback.onResponse(
                             new PrepareGetCredentialResponseInternal(
-                            false, null,
-                            false, false, null));
+                                    false, null,
+                                    false, false, null));
                 } catch (RemoteException e) {
                     Log.i(
                             TAG,
@@ -540,10 +546,10 @@
                 List<CredentialOption> optionsThatRequireActiveCredentials =
                         request.getCredentialOptions().stream()
                                 .filter(credentialOption -> credentialOption
-                                                .getCredentialRetrievalData()
-                                                .getStringArrayList(
-                                                        CredentialOption
-                                                                .SUPPORTED_ELEMENT_KEYS) != null)
+                                        .getCredentialRetrievalData()
+                                        .getStringArrayList(
+                                                CredentialOption
+                                                        .SUPPORTED_ELEMENT_KEYS) != null)
                                 .toList();
 
                 List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
@@ -614,6 +620,8 @@
             final CreateRequestSession session =
                     new CreateRequestSession(
                             getContext(),
+                            mSessionManager,
+                            mLock,
                             userId,
                             callingUid,
                             request,
@@ -621,6 +629,7 @@
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
                             CancellationSignal.fromTransport(cancelTransport),
                             timestampBegan);
+            addSessionLocked(userId, session);
 
             processCreateCredential(request, callback, session);
             return cancelTransport;
@@ -775,6 +784,19 @@
                     mContext, userId, providerFilter, getEnabledProviders());
         }
 
+        @Override
+        public boolean isServiceEnabled() {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                return DeviceConfig.getBoolean(
+                        DeviceConfig.NAMESPACE_CREDENTIAL,
+                        CredentialManager.DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
+                        false);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
         @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
         // this.mLock
         private Set<ComponentName> getEnabledProviders() {
@@ -815,6 +837,8 @@
             final ClearRequestSession session =
                     new ClearRequestSession(
                             getContext(),
+                            mSessionManager,
+                            mLock,
                             userId,
                             callingUid,
                             callback,
@@ -822,6 +846,7 @@
                             constructCallingAppInfo(callingPackage, userId, null),
                             CancellationSignal.fromTransport(cancelTransport),
                             timestampBegan);
+            addSessionLocked(userId, session);
 
             // Initiate all provider sessions
             // TODO: Determine if provider needs to have clear capability in their manifest
@@ -905,6 +930,13 @@
         }
     }
 
+    private void addSessionLocked(@UserIdInt int userId,
+            RequestSession requestSession) {
+        synchronized (mLock) {
+            mSessionManager.addSession(userId, requestSession.mRequestId, requestSession);
+        }
+    }
+
     private void enforceCallingPackage(String callingPackage, int callingUid) {
         int packageUid;
         PackageManager pm = mContext.createContextAsUser(
@@ -919,4 +951,23 @@
             throw new SecurityException(callingPackage + " does not belong to uid " + callingUid);
         }
     }
+
+    private class SessionManager implements RequestSession.SessionLifetime {
+        @Override
+        @GuardedBy("mLock")
+        public void onFinishRequestSession(@UserIdInt int userId, IBinder token) {
+            Log.i(TAG, "In onFinishRequestSession");
+            if (mRequestSessions.get(userId) != null) {
+                mRequestSessions.get(userId).remove(token);
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void addSession(int userId, IBinder token, RequestSession requestSession) {
+            if (mRequestSessions.get(userId) == null) {
+                mRequestSessions.put(userId, new HashMap<>());
+            }
+            mRequestSessions.get(userId).put(token, requestSession);
+        }
+    }
 }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 546c37f..e16d48e 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -30,6 +30,7 @@
 import android.credentials.ui.UserSelectionDialogResult;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.ResultReceiver;
 import android.service.credentials.CredentialProviderInfoFactory;
@@ -50,6 +51,20 @@
     @NonNull private final Context mContext;
     // TODO : Use for starting the activity for this user
     private final int mUserId;
+
+    private UiStatus mStatus;
+
+    /** Creates intent that is ot be invoked to cancel an in-progress UI session. */
+    public Intent createCancelIntent(IBinder requestId, String packageName) {
+        return IntentFactory.createCancelUiIntent(requestId, /*shouldShowCancellationUi=*/ true,
+                packageName);
+    }
+
+    enum UiStatus {
+        IN_PROGRESS,
+        USER_INTERACTION,
+        NOT_STARTED, TERMINATED
+    }
     @NonNull private final ResultReceiver mResultReceiver = new ResultReceiver(
             new Handler(Looper.getMainLooper())) {
         @Override
@@ -61,6 +76,7 @@
     private void handleUiResult(int resultCode, Bundle resultData) {
         switch (resultCode) {
             case UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION:
+                mStatus = UiStatus.IN_PROGRESS;
                 UserSelectionDialogResult selection = UserSelectionDialogResult
                         .fromResultData(resultData);
                 if (selection != null) {
@@ -70,16 +86,20 @@
                 }
                 break;
             case UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED:
+                mStatus = UiStatus.TERMINATED;
                 mCallbacks.onUiCancellation(/* isUserCancellation= */ true);
                 break;
             case UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS:
+                mStatus = UiStatus.TERMINATED;
                 mCallbacks.onUiCancellation(/* isUserCancellation= */ false);
                 break;
             case UserSelectionDialogResult.RESULT_CODE_DATA_PARSING_FAILURE:
+                mStatus = UiStatus.TERMINATED;
                 mCallbacks.onUiSelectorInvocationFailure();
                 break;
             default:
                 Slog.i(TAG, "Unknown error code returned from the UI");
+                mStatus = UiStatus.IN_PROGRESS;
                 mCallbacks.onUiSelectorInvocationFailure();
                 break;
         }
@@ -103,6 +123,17 @@
         mContext = context;
         mUserId = userId;
         mCallbacks = callbacks;
+        mStatus = UiStatus.IN_PROGRESS;
+    }
+
+    /** Set status for credential manager UI */
+    public void setStatus(UiStatus status) {
+        mStatus = status;
+    }
+
+    /** Returns status for credential manager UI */
+    public UiStatus getStatus() {
+        return mStatus;
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index c0c7be9..2548bd8 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -45,12 +45,13 @@
         IGetCredentialCallback, GetCredentialResponse>
         implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
     private static final String TAG = "GetRequestSession";
-    public GetRequestSession(Context context, int userId, int callingUid,
+    public GetRequestSession(Context context, RequestSession.SessionLifetime sessionCallback,
+            Object lock, int userId, int callingUid,
             IGetCredentialCallback callback, GetCredentialRequest request,
             CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
             long startedTimestamp) {
-        super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET,
-                callingAppInfo, cancellationSignal, startedTimestamp);
+        super(context, sessionCallback, lock, userId, callingUid, request, callback,
+                RequestInfo.TYPE_GET, callingAppInfo, cancellationSignal, startedTimestamp);
         int numTypes = (request.getCredentialOptions().stream()
                 .map(CredentialOption::getType).collect(
                 Collectors.toSet())).size(); // Dedupe type strings
@@ -81,6 +82,7 @@
     @Override
     protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
         mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
+        mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
         try {
             mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
                     RequestInfo.newGetRequestInfo(
@@ -88,6 +90,7 @@
                     providerDataList));
         } catch (RemoteException e) {
             mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
+            mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
             respondToClientWithErrorAndFinish(
                     GetCredentialException.TYPE_UNKNOWN, "Unable to instantiate selector");
         }
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index c4e480a..88f3e6c 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -49,14 +49,13 @@
 
     private final IPrepareGetCredentialCallback mPrepareGetCredentialCallback;
 
-    public PrepareGetRequestSession(Context context, int userId, int callingUid,
-            IGetCredentialCallback callback,
-            GetCredentialRequest request,
-            CallingAppInfo callingAppInfo,
-            CancellationSignal cancellationSignal, long startedTimestamp,
-            IPrepareGetCredentialCallback prepareGetCredentialCallback) {
-        super(context, userId, callingUid, callback, request, callingAppInfo, cancellationSignal,
-                startedTimestamp);
+    public PrepareGetRequestSession(Context context,
+            RequestSession.SessionLifetime sessionCallback, Object lock, int userId,
+            int callingUid, IGetCredentialCallback getCredCallback, GetCredentialRequest request,
+            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+            long startedTimestamp, IPrepareGetCredentialCallback prepareGetCredentialCallback) {
+        super(context, sessionCallback, lock, userId, callingUid, getCredCallback, request,
+                callingAppInfo, cancellationSignal, startedTimestamp);
         int numTypes = (request.getCredentialOptions().stream()
                 .map(CredentialOption::getType).collect(
                         Collectors.toSet())).size(); // Dedupe type strings
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index 1b736e0..eaf58f1 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -126,7 +126,8 @@
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
             startCandidateMetrics();
-            mRemoteCredentialService.onClearCredentialState(mProviderRequest, this);
+            mProviderCancellationSignal =
+                    mRemoteCredentialService.onClearCredentialState(mProviderRequest, this);
         }
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index bef045f..c657e3b 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -236,7 +236,8 @@
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
             startCandidateMetrics();
-            mRemoteCredentialService.onCreateCredential(mProviderRequest, this);
+            mProviderCancellationSignal =
+                    mRemoteCredentialService.onCreateCredential(mProviderRequest, this);
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 427a894..9c9c0c2 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -302,7 +302,9 @@
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
             startCandidateMetrics();
-            mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this);
+            mProviderCancellationSignal =
+                    mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this);
+            boolean foundSig = mProviderCancellationSignal == null;
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 8c0e1c1..d165756 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -30,6 +30,7 @@
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.server.credentials.metrics.ProviderSessionMetric;
 
@@ -189,7 +190,7 @@
             }
             setStatus(Status.CANCELED);
         } catch (RemoteException e) {
-            Log.i(TAG, "Issue while cancelling provider session: " + e.getMessage());
+            Slog.e(TAG, "Issue while cancelling provider session: ", e);
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index cfb9ad4..ed175ed 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -20,6 +20,7 @@
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.credentials.CredentialProviderInfo;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.UserSelectionDialogResult;
@@ -29,8 +30,10 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.service.credentials.CallingAppInfo;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.server.credentials.metrics.ApiName;
@@ -39,8 +42,8 @@
 import com.android.server.credentials.metrics.RequestSessionMetric;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Base class of a request session, that listens to UI events. This class must be extended
@@ -49,6 +52,11 @@
 abstract class RequestSession<T, U, V> implements CredentialManagerUi.CredentialManagerUiCallback {
     private static final String TAG = "RequestSession";
 
+    public interface SessionLifetime {
+        /** Called when the user makes a selection. */
+        void onFinishRequestSession(@UserIdInt int userId, IBinder token);
+    }
+
     // TODO: Revise access levels of attributes
     @NonNull
     protected final T mClientRequest;
@@ -72,10 +80,14 @@
     @NonNull
     protected final CancellationSignal mCancellationSignal;
 
-    protected final Map<String, ProviderSession> mProviders = new HashMap<>();
+    protected final Map<String, ProviderSession> mProviders = new ConcurrentHashMap<>();
     protected final RequestSessionMetric mRequestSessionMetric = new RequestSessionMetric();
     protected final String mHybridService;
 
+    protected final Object mLock;
+
+    protected final SessionLifetime mSessionCallback;
+
     @NonNull
     protected RequestSessionStatus mRequestSessionStatus =
             RequestSessionStatus.IN_PROGRESS;
@@ -91,11 +103,15 @@
     }
 
     protected RequestSession(@NonNull Context context,
-            @UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback,
+            RequestSession.SessionLifetime sessionCallback,
+            Object lock, @UserIdInt int userId, int callingUid,
+            @NonNull T clientRequest, U clientCallback,
             @NonNull String requestType,
             CallingAppInfo callingAppInfo,
             CancellationSignal cancellationSignal, long timestampStarted) {
         mContext = context;
+        mLock = lock;
+        mSessionCallback = sessionCallback;
         mUserId = userId;
         mCallingUid = callingUid;
         mClientRequest = clientRequest;
@@ -111,6 +127,32 @@
                 R.string.config_defaultCredentialManagerHybridService);
         mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mRequestId,
                 mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType));
+        setCancellationListener();
+    }
+
+    private void setCancellationListener() {
+        mCancellationSignal.setOnCancelListener(
+                () -> {
+                    boolean isUiActive = maybeCancelUi();
+                    finishSession(!isUiActive);
+                }
+        );
+    }
+
+    private boolean maybeCancelUi() {
+        if (mCredentialManagerUi.getStatus()
+                == CredentialManagerUi.UiStatus.USER_INTERACTION) {
+            final long originalCallingUidToken = Binder.clearCallingIdentity();
+            try {
+                mContext.startActivityAsUser(mCredentialManagerUi.createCancelIntent(
+                                mRequestId, mClientAppInfo.getPackageName())
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserHandle.of(mUserId));
+                return true;
+            } finally {
+                Binder.restoreCallingIdentity(originalCallingUidToken);
+            }
+        }
+        return false;
     }
 
     public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
@@ -154,12 +196,19 @@
     }
 
     protected void finishSession(boolean propagateCancellation) {
-        Log.i(TAG, "finishing session");
+        Slog.d(TAG, "finishing session with propagateCancellation " + propagateCancellation);
         if (propagateCancellation) {
             mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession);
         }
         mRequestSessionStatus = RequestSessionStatus.COMPLETE;
         mProviders.clear();
+        clearRequestSessionLocked();
+    }
+
+    private void clearRequestSessionLocked() {
+        synchronized (mLock) {
+            mSessionCallback.onFinishRequestSession(mUserId, mRequestId);
+        }
     }
 
     protected boolean isAnyProviderPending() {
@@ -194,7 +243,6 @@
         ArrayList<ProviderData> providerDataList = getProviderDataForUi();
         if (!providerDataList.isEmpty()) {
             Log.i(TAG, "provider list not empty about to initiate ui");
-            mRequestSessionMetric.logCandidatePhaseMetrics(mProviders);
             launchUiWithProviderData(providerDataList);
         }
     }
@@ -204,9 +252,9 @@
         Log.i(TAG, "In getProviderDataAndInitiateUi");
         Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size());
         ArrayList<ProviderData> providerDataList = new ArrayList<>();
+        mRequestSessionMetric.logCandidatePhaseMetrics(mProviders);
 
         if (isSessionCancelled()) {
-            mRequestSessionMetric.logCandidatePhaseMetrics(mProviders);
             finishSession(/*propagateCancellation=*/true);
             return providerDataList;
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a109dee..b215641 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -92,6 +92,8 @@
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
+import static android.app.admin.DeviceAdminInfo.USES_POLICY_FORCE_LOCK;
+import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA;
 import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY;
@@ -439,6 +441,7 @@
 import android.util.DebugUtils;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
+import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -773,6 +776,14 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     public static final long EXPLICIT_WIPE_BEHAVIOUR = 242193913L;
 
+    /**
+     * Apps targetting U+ should now expect that attempts to grant sensor permissions without
+     * authorisation will result in a security exception.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final long THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS = 277035314L;
+
     // Only add to the end of the list. Do not change or rearrange these values, that will break
     // historical data. Do not use negative numbers or zero, logger only handles positive
     // integers.
@@ -2819,6 +2830,16 @@
         return doAdmin;
     }
 
+    ActiveAdmin getDefaultDeviceOwnerLocked(@UserIdInt int userId) {
+        ensureLocked();
+        ComponentName doComponent = mOwners.getDeviceOwnerComponent();
+        if (mOwners.getDeviceOwnerType(doComponent.getPackageName()) == DEFAULT_DEVICE_OWNER) {
+            ActiveAdmin doAdmin = getUserData(userId).mAdminMap.get(doComponent);
+            return doAdmin;
+        }
+        return null;
+    }
+
     ActiveAdmin getProfileOwnerLocked(@UserIdInt int userId) {
         ensureLocked();
         final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId);
@@ -2848,6 +2869,18 @@
         return getDeviceOwnerLocked(userId);
     }
 
+    ActiveAdmin getProfileOwnerOrDefaultDeviceOwnerLocked(@UserIdInt int userId) {
+        ensureLocked();
+        // Try to find an admin which can use reqPolicy
+        final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId);
+
+        if (poAdminComponent != null) {
+            return getProfileOwnerLocked(userId);
+        }
+
+        return getDefaultDeviceOwnerLocked(userId);
+    }
+
     @NonNull ActiveAdmin getParentOfAdminIfRequired(ActiveAdmin admin, boolean parent) {
         Objects.requireNonNull(admin);
         return parent ? admin.getParentActiveAdmin() : admin;
@@ -5337,9 +5370,12 @@
                     saveSettingsLocked(caller.getUserId());
                 });
 
+
+                //TODO(b/276855301): caller.getPackageName() will be null when the coexistence flags are
+                // turned off. Change back to caller.getPackageName once this API is unflagged.
                 DevicePolicyEventLogger
                         .createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY)
-                        .setAdmin(caller.getPackageName())
+                        .setAdmin(admin.info.getPackageName())
                         .setInt(passwordComplexity)
                         .setBoolean(calledOnParent)
                         .write();
@@ -5974,8 +6010,13 @@
     }
 
     @Override
-    public void lockNow(int flags, boolean parent) {
-        final CallerIdentity caller = getCallerIdentity();
+    public void lockNow(int flags, String callerPackageName, boolean parent) {
+        CallerIdentity caller;
+        if (isPermissionCheckFlagEnabled()) {
+            caller = getCallerIdentity(callerPackageName);
+        } else {
+            caller = getCallerIdentity();
+        }
 
         final int callingUserId = caller.getUserId();
         ComponentName adminComponent = null;
@@ -5984,11 +6025,13 @@
             // Make sure the caller has any active admin with the right policy or
             // the required permission.
             if (isPermissionCheckFlagEnabled()) {
-                admin = getActiveAdminOrCheckPermissionsForCallerLocked(
-                        null,
-                        DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
-                        parent,
-                        Set.of(MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE));
+                admin = enforcePermissionAndGetEnforcingAdmin(
+                        /* admin= */ null,
+                        /* permission= */ MANAGE_DEVICE_POLICY_LOCK,
+                        USES_POLICY_FORCE_LOCK,
+                        caller.getPackageName(),
+                        getAffectedUser(parent)
+                 ).getActiveAdmin();
             } else {
                 admin = getActiveAdminOrCheckPermissionForCallerLocked(
                         null,
@@ -7481,7 +7524,8 @@
         if (isPolicyEngineForFinanceFlagEnabled()) {
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     /*admin=*/ null,
-                    MANAGE_DEVICE_POLICY_WIPE_DATA,
+                    /*permission= */ MANAGE_DEVICE_POLICY_WIPE_DATA,
+                    USES_POLICY_WIPE_DATA,
                     caller.getPackageName(),
                     factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
             admin = enforcingAdmin.getActiveAdmin();
@@ -8511,7 +8555,7 @@
                         isProfileOwnerOfOrganizationOwnedDevice(caller));
             } else {
                 Preconditions.checkCallAuthorization(isProfileOwner(caller)
-                        || isDeviceOwner(caller));
+                        || isDefaultDeviceOwner(caller));
             }
         }
 
@@ -8525,7 +8569,7 @@
                         targetUserId).getActiveAdmin();
             } else {
                 ap = getParentOfAdminIfRequired(
-                        getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
+                        getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent);
             }
 
             if (ap.disableScreenCapture != disabled) {
@@ -8557,15 +8601,21 @@
         }
         if (admin != null && admin.disableScreenCapture) {
             setScreenCaptureDisabled(UserHandle.USER_ALL);
-        } else {
-            // Otherwise, update screen capture only for the calling user.
-            admin = getProfileOwnerAdminLocked(adminUserId);
-            if (admin != null && admin.disableScreenCapture) {
-                setScreenCaptureDisabled(adminUserId);
-            } else {
-                setScreenCaptureDisabled(UserHandle.USER_NULL);
-            }
+            return;
         }
+        // Otherwise, update screen capture only for the calling user.
+        admin = getProfileOwnerAdminLocked(adminUserId);
+        if (admin != null && admin.disableScreenCapture) {
+            setScreenCaptureDisabled(adminUserId);
+            return;
+        }
+        // If the admin is permission based, update only for the calling user.
+        admin = getUserData(adminUserId).createOrGetPermissionBasedAdmin(adminUserId);
+        if (admin != null && admin.disableScreenCapture) {
+            setScreenCaptureDisabled(adminUserId);
+            return;
+        }
+        setScreenCaptureDisabled(UserHandle.USER_NULL);
     }
 
     // Set the latest screen capture policy, overriding any existing ones.
@@ -11424,7 +11474,7 @@
                     RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(callerUserId),
                     AsyncTask.THREAD_POOL_EXECUTOR, callback);
             try {
-                future.get(5, TimeUnit.SECONDS);
+                future.get(20, TimeUnit.SECONDS);
             } catch (TimeoutException e) {
                 throw new IllegalArgumentException("Timeout when setting the app as the dialer", e);
             } catch (ExecutionException e) {
@@ -16466,6 +16516,25 @@
                     MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
                     callerPackage,
                     caller.getUserId());
+            if (SENSOR_PERMISSIONS.contains(permission)
+                    && grantState == PERMISSION_GRANT_STATE_GRANTED
+                    && (!canAdminGrantSensorsPermissions() || isCallerDelegate(caller))) {
+                if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS,
+                        caller.getPackageName(), caller.getUserId())) {
+                    throw new SecurityException(
+                            "Caller not permitted to grant sensor permissions.");
+                } else {
+                    // This is to match the legacy behaviour.
+                    callback.sendResult(Bundle.EMPTY);
+                    return;
+                }
+            }
+            // Check all the states where Exceptions aren't thrown but the permission
+            // isn't granted either.
+            if (!canGrantPermission(caller, permission, packageName)) {
+                callback.sendResult(null);
+                return;
+            }
             // TODO(b/266924257): decide how to handle the internal state if the package doesn't
             //  exist, or the permission isn't requested by the app, because we could end up with
             //  inconsistent state between the policy engine and package manager. Also a package
@@ -16541,6 +16610,41 @@
         }
     }
 
+    private static final List<String> SENSOR_PERMISSIONS = new ArrayList<>();
+    {
+        SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+        SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+        SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+        SENSOR_PERMISSIONS.add(Manifest.permission.CAMERA);
+        SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
+        SENSOR_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
+        SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+        SENSOR_PERMISSIONS.add(Manifest.permission.BACKGROUND_CAMERA);
+        SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_BACKGROUND_AUDIO);
+        SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+        SENSOR_PERMISSIONS.add(
+                Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
+        SENSOR_PERMISSIONS.add(
+                    Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
+    }
+
+    private boolean canGrantPermission(CallerIdentity caller, String permission,
+            String targetPackageName) {
+        boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+                >= android.os.Build.VERSION_CODES.Q;
+        if (!isPostQAdmin) {
+            // Legacy admins assume that they cannot control pre-M apps
+            if (getTargetSdk(targetPackageName, caller.getUserId())
+                    < android.os.Build.VERSION_CODES.M) {
+                return false;
+            }
+        }
+        if (!isRuntimePermission(permission)) {
+            return false;
+        }
+        return true;
+    }
+
     private void enforcePermissionGrantStateOnFinancedDevice(
             String packageName, String permission) {
         if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) {
@@ -17637,7 +17741,6 @@
 
         synchronized (getLockObject()) {
             if (isPermissionCheckFlagEnabled()) {
-                // TODO: add support for DELEGATION_SECURITY_LOGGING
                 enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
                         UserHandle.USER_ALL);
             } else {
@@ -17718,7 +17821,8 @@
 
         final CallerIdentity caller = getCallerIdentity(admin, packageName);
         if (isPermissionCheckFlagEnabled()) {
-            // TODO: Restore the "affiliated users" check
+            Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+                    || areAllUsersAffiliatedWithDeviceLocked());
             enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
                     UserHandle.USER_ALL);
         } else {
@@ -17770,7 +17874,9 @@
 
         final CallerIdentity caller = getCallerIdentity(admin, packageName);
         if (isPermissionCheckFlagEnabled()) {
-            // TODO: Restore the "affiliated users" check
+            Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+                    || areAllUsersAffiliatedWithDeviceLocked());
+
             enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
                     UserHandle.USER_ALL);
         } else {
@@ -22321,244 +22427,208 @@
 
     // Permissions of existing DPC types.
     private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of(
-            MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
+            MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
             MANAGE_DEVICE_POLICY_ACROSS_USERS,
+            MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
             MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
-            SET_TIME,
-            SET_TIME_ZONE,
-            MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_WIFI,
-            MANAGE_DEVICE_POLICY_WIPE_DATA,
-            MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-            MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
-            MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
-            MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
-            MANAGE_DEVICE_POLICY_MTE,
-            MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-            MANAGE_DEVICE_POLICY_LOCK,
-            MANAGE_DEVICE_POLICY_FACTORY_RESET,
-            MANAGE_DEVICE_POLICY_KEYGUARD,
-            MANAGE_DEVICE_POLICY_CERTIFICATES,
-            MANAGE_DEVICE_POLICY_KEYGUARD,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
-            MANAGE_DEVICE_POLICY_APPS_CONTROL,
-            MANAGE_DEVICE_POLICY_LOCK_TASK,
-            MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-            MANAGE_DEVICE_POLICY_CAMERA,
-            MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
-            MANAGE_DEVICE_POLICY_DEFAULT_SMS,
-            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-            MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
-            MANAGE_DEVICE_POLICY_RESET_PASSWORD,
-            MANAGE_DEVICE_POLICY_STATUS_BAR,
-            MANAGE_DEVICE_POLICY_LOCK_TASK,
-            MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
             MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
+            MANAGE_DEVICE_POLICY_APPS_CONTROL,
+            MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
             MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
             MANAGE_DEVICE_POLICY_AUTOFILL,
             MANAGE_DEVICE_POLICY_BLUETOOTH,
             MANAGE_DEVICE_POLICY_CALLS,
             MANAGE_DEVICE_POLICY_CAMERA,
+            MANAGE_DEVICE_POLICY_CERTIFICATES,
+            MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
             MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+            MANAGE_DEVICE_POLICY_DEFAULT_SMS,
             MANAGE_DEVICE_POLICY_DISPLAY,
             MANAGE_DEVICE_POLICY_FACTORY_RESET,
             MANAGE_DEVICE_POLICY_FUN,
             MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+            MANAGE_DEVICE_POLICY_KEYGUARD,
             MANAGE_DEVICE_POLICY_LOCALE,
             MANAGE_DEVICE_POLICY_LOCATION,
+            MANAGE_DEVICE_POLICY_LOCK,
             MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
+            MANAGE_DEVICE_POLICY_LOCK_TASK,
             MANAGE_DEVICE_POLICY_MICROPHONE,
             MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+            MANAGE_DEVICE_POLICY_MTE,
             MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+            MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
             MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
             MANAGE_DEVICE_POLICY_PRINTING,
-            MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
             MANAGE_DEVICE_POLICY_PROFILES,
             MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+            MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+            MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
+            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
             MANAGE_DEVICE_POLICY_SAFE_BOOT,
+            MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
             MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
+            MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
             MANAGE_DEVICE_POLICY_SMS,
+            MANAGE_DEVICE_POLICY_STATUS_BAR,
+            MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
             MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+            MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
             MANAGE_DEVICE_POLICY_TIME,
+            MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
             MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
             MANAGE_DEVICE_POLICY_USERS,
             MANAGE_DEVICE_POLICY_VPN,
             MANAGE_DEVICE_POLICY_WALLPAPER,
             MANAGE_DEVICE_POLICY_WIFI,
             MANAGE_DEVICE_POLICY_WINDOWS,
-            MANAGE_DEVICE_POLICY_APP_RESTRICTIONS
+            MANAGE_DEVICE_POLICY_WIPE_DATA,
+            SET_TIME,
+            SET_TIME_ZONE
     );
     private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of(
-            MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
             MANAGE_DEVICE_POLICY_ACROSS_USERS,
+            MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
             MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
-            MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-            MANAGE_DEVICE_POLICY_FACTORY_RESET,
-            MANAGE_DEVICE_POLICY_KEYGUARD,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
             MANAGE_DEVICE_POLICY_APPS_CONTROL,
-            MANAGE_DEVICE_POLICY_LOCK_TASK,
             MANAGE_DEVICE_POLICY_CALLS,
             MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+            MANAGE_DEVICE_POLICY_FACTORY_RESET,
             MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
-            MANAGE_DEVICE_POLICY_USERS,
+            MANAGE_DEVICE_POLICY_KEYGUARD,
+            MANAGE_DEVICE_POLICY_LOCK_TASK,
+            MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
             MANAGE_DEVICE_POLICY_SAFE_BOOT,
+            MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
             MANAGE_DEVICE_POLICY_TIME,
-            MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS);
-    private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+            MANAGE_DEVICE_POLICY_USERS,
+            MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS
+    );
+
+    /**
+     * All the permisisons granted to a profile owner.
+     */
+    private static final List<String> PROFILE_OWNER_PERMISSIONS  =
             List.of(
-                MANAGE_DEVICE_POLICY_ACROSS_USERS,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
-                SET_TIME,
-                SET_TIME_ZONE,
-                MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-                MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-                MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
-                MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                MANAGE_DEVICE_POLICY_WIFI,
-                MANAGE_DEVICE_POLICY_WIPE_DATA,
-                MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-                MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
-                MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
-                MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
-                MANAGE_DEVICE_POLICY_MTE,
-                MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-                MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                MANAGE_DEVICE_POLICY_LOCK,
-                MANAGE_DEVICE_POLICY_FACTORY_RESET,
-                MANAGE_DEVICE_POLICY_KEYGUARD,
-                MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
                 MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
+                MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
                 MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
                 MANAGE_DEVICE_POLICY_AUTOFILL,
-                MANAGE_DEVICE_POLICY_BLUETOOTH,
                 MANAGE_DEVICE_POLICY_CALLS,
-                MANAGE_DEVICE_POLICY_CAMERA,
                 MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
                 MANAGE_DEVICE_POLICY_DISPLAY,
                 MANAGE_DEVICE_POLICY_FACTORY_RESET,
                 MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+                MANAGE_DEVICE_POLICY_KEYGUARD,
                 MANAGE_DEVICE_POLICY_LOCALE,
                 MANAGE_DEVICE_POLICY_LOCATION,
+                MANAGE_DEVICE_POLICY_LOCK,
                 MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-                MANAGE_DEVICE_POLICY_MICROPHONE,
-                MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
                 MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
-                MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+                MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+                MANAGE_DEVICE_POLICY_PACKAGE_STATE,
                 MANAGE_DEVICE_POLICY_PRINTING,
                 MANAGE_DEVICE_POLICY_PROFILES,
                 MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
-                MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
-                MANAGE_DEVICE_POLICY_SAFE_BOOT,
+                MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+                MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
+                MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
                 MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
-                MANAGE_DEVICE_POLICY_SMS,
+                MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
                 MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
                 MANAGE_DEVICE_POLICY_TIME,
                 MANAGE_DEVICE_POLICY_VPN,
-                MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
-                MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
-                MANAGE_DEVICE_POLICY_DEFAULT_SMS,
-                MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                MANAGE_DEVICE_POLICY_RESET_PASSWORD,
-                MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
-                MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
-                MANAGE_DEVICE_POLICY_KEYGUARD,
-                MANAGE_DEVICE_POLICY_WIFI,
-                MANAGE_DEVICE_POLICY_WIPE_DATA,
-                MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-                MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
-                MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
-                MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
-                MANAGE_DEVICE_POLICY_MTE,
-                MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                MANAGE_DEVICE_POLICY_LOCK,
-                MANAGE_DEVICE_POLICY_FACTORY_RESET,
-                MANAGE_DEVICE_POLICY_KEYGUARD,
-                MANAGE_DEVICE_POLICY_CERTIFICATES);
-    private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS  = List.of(
-            SET_TIME,
-            SET_TIME_ZONE,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
-            MANAGE_DEVICE_POLICY_APPS_CONTROL,
-            MANAGE_DEVICE_POLICY_LOCK_TASK,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_WIPE_DATA,
-            MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-            MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-            MANAGE_DEVICE_POLICY_LOCK,
-            MANAGE_DEVICE_POLICY_KEYGUARD,
-            MANAGE_DEVICE_POLICY_LOCK_TASK,
-            MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
-            MANAGE_DEVICE_POLICY_BLUETOOTH,
-            MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
-            MANAGE_DEVICE_POLICY_FACTORY_RESET,
-            MANAGE_DEVICE_POLICY_FUN,
-            MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
-            MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
-            MANAGE_DEVICE_POLICY_USERS,
-            MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
-            MANAGE_DEVICE_POLICY_SAFE_BOOT,
-            MANAGE_DEVICE_POLICY_SMS,
-            MANAGE_DEVICE_POLICY_TIME,
-            MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
-            MANAGE_DEVICE_POLICY_WINDOWS,
-            MANAGE_DEVICE_POLICY_LOCK_TASK,
-            MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-            MANAGE_DEVICE_POLICY_CAMERA,
-            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-            MANAGE_DEVICE_POLICY_RESET_PASSWORD,
-            MANAGE_DEVICE_POLICY_STATUS_BAR,
-            MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
-            MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS);
-    private static final List<String> PROFILE_OWNER_PERMISSIONS  = List.of(
-            MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
-            MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
-            MANAGE_DEVICE_POLICY_APPS_CONTROL,
-            MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-            MANAGE_DEVICE_POLICY_WIPE_DATA,
-            MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-            MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-            MANAGE_DEVICE_POLICY_LOCK,
-            MANAGE_DEVICE_POLICY_KEYGUARD,
-            MANAGE_DEVICE_POLICY_APPS_CONTROL,
-            MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-            MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
-            MANAGE_DEVICE_POLICY_AUTOFILL,
-            MANAGE_DEVICE_POLICY_CALLS,
-            MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
-            MANAGE_DEVICE_POLICY_DISPLAY,
-            MANAGE_DEVICE_POLICY_FACTORY_RESET,
-            MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
-            MANAGE_DEVICE_POLICY_LOCALE,
-            MANAGE_DEVICE_POLICY_LOCATION,
-            MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-            MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
-            MANAGE_DEVICE_POLICY_PRINTING,
-            MANAGE_DEVICE_POLICY_PROFILES,
-            MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
-            MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
-            MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
-            MANAGE_DEVICE_POLICY_TIME,
-            MANAGE_DEVICE_POLICY_VPN,
-            MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-            MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
-            MANAGE_DEVICE_POLICY_RESET_PASSWORD,
-            MANAGE_DEVICE_POLICY_APP_RESTRICTIONS
+                MANAGE_DEVICE_POLICY_WIPE_DATA
             );
 
+    /**
+    * All the additional permissions granted to an organisation owned profile owner.
+    */
+    private static final List<String>
+            ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+                List.of(
+                    MANAGE_DEVICE_POLICY_ACROSS_USERS,
+                    MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
+                    MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                    MANAGE_DEVICE_POLICY_BLUETOOTH,
+                    MANAGE_DEVICE_POLICY_CAMERA,
+                    MANAGE_DEVICE_POLICY_CERTIFICATES,
+                    MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
+                    MANAGE_DEVICE_POLICY_DEFAULT_SMS,
+                    MANAGE_DEVICE_POLICY_LOCALE,
+                    MANAGE_DEVICE_POLICY_MICROPHONE,
+                    MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+                    MANAGE_DEVICE_POLICY_MTE,
+                    MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+                    MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+                    MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
+                    MANAGE_DEVICE_POLICY_SAFE_BOOT,
+                    MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+                    MANAGE_DEVICE_POLICY_SMS,
+                    MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
+                    MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+                    MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
+                    MANAGE_DEVICE_POLICY_WIFI,
+                    SET_TIME,
+                    SET_TIME_ZONE
+                );
+
+
+    private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
+            List.of(
+                    MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
+                    MANAGE_DEVICE_POLICY_BLUETOOTH,
+                    MANAGE_DEVICE_POLICY_CAMERA,
+                    MANAGE_DEVICE_POLICY_DISPLAY,
+                    MANAGE_DEVICE_POLICY_FUN,
+                    MANAGE_DEVICE_POLICY_LOCK_TASK,
+                    MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+                    MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+                    MANAGE_DEVICE_POLICY_PRINTING,
+                    MANAGE_DEVICE_POLICY_PROFILES,
+                    MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+                    MANAGE_DEVICE_POLICY_SAFE_BOOT,
+                    MANAGE_DEVICE_POLICY_SMS,
+                    MANAGE_DEVICE_POLICY_STATUS_BAR,
+                    MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+                    MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
+                    MANAGE_DEVICE_POLICY_USERS,
+                    MANAGE_DEVICE_POLICY_WINDOWS,
+                    SET_TIME,
+                    SET_TIME_ZONE
+            );
+
+    /**
+     * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+     * {@link ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS}.
+     */
+    private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+            new ArrayList();
+
+    /**
+     * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+     * {@link ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS}.
+     */
+    private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS  =
+            new ArrayList();
+
+
     private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
     {
+        // Organisation owned profile owners have all the permission of a profile owner plus
+        // some extra permissions.
+        PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
+        PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS.addAll(
+                ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS);
+        // Profile owners on user 0 have all the permission of a profile owner plus
+        // some extra permissions.
+        PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
+        PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS);
+
         DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS);
         DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS);
         DPC_PERMISSIONS.put(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
@@ -22566,14 +22636,6 @@
         DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS);
         DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS);
     }
-
-    // Map of permission Active admin DEVICE_POLICY.
-    //TODO(b/254253251) Fill this map in as new permissions are added for policies.
-    private static final HashMap<String, Integer> ACTIVE_ADMIN_POLICIES = new HashMap<>();
-    {
-        //Any ActiveAdmin is able to call the support message APIs without certain policies.
-        ACTIVE_ADMIN_POLICIES.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, null);
-    }
     //Map of Permission to Delegate Scope.
     private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>();
     {
@@ -22587,75 +22649,40 @@
     private static final HashMap<String, String> CROSS_USER_PERMISSIONS =
             new HashMap<>();
     {
-        // Time and Timezone is intrinsically global so there is no cross-user permission.
+        // The permissions are all intrinsically global and therefore have no cross-user permission.
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET, null);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MTE, null);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, null);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_STATUS_BAR, null);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, null);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, null);
         CROSS_USER_PERMISSIONS.put(SET_TIME, null);
         CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null);
-        // system updates are intrinsically global so there is no cross-user permission
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, null);
-        // security logs are intrinsically global so there is no cross-user permission
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, null);
-        // usb signalling is intrinsically global so there is no cross-user permission
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, null);
-        // mte is intrinsically global so there is no cross-user permission
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MTE, null);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET, null);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_STATUS_BAR, null);
-        // Organisation identity policy will involve data of other organisations on the device and
-        // therefore the FULL cross-user permission is required.
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS);
+
+        // The permissions are all critical for securing data within the current user and
+        // therefore are protected with MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL for
+        // cross-user calls.
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_KEYGUARD,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(
-                MANAGE_DEVICE_POLICY_LOCK, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(
-                MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
-        // Granting runtime permissions can grant applications significant powers therefore the FULL
-        // cross-user permission is required.
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+
+        // These permissions are required for securing device ownership without accessing user data
+        // and therefore are protected with MANAGE_DEVICE_POLICY_ACROSS_USERS for cross-user calls.
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_BLUETOOTH,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CALLS,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CAMERA,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DISPLAY,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FUN,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCALE,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCATION,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEFAULT_SMS,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
@@ -22664,46 +22691,77 @@
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PRINTING,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILES,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SAFE_BOOT,
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SMS,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SAFE_BOOT,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_TIME,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS);
+
+        // These permissions may grant access to user data and therefore must be protected with
+        // MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL for cross-user calls.
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DISPLAY,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FUN,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCALE,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCATION,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILES,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PRINTING,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USERS,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_VPN,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WALLPAPER,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WINDOWS,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEFAULT_SMS,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
-        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESET_PASSWORD,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
     }
 
     /**
@@ -22727,6 +22785,26 @@
     }
 
     /**
+     * Checks if the calling process has been granted permission to apply a device policy on a
+     * specific user.
+     * The given permission will be checked along with its associated cross-user permission if it
+     * exists and the target user is different to the calling user.
+     * Returns an {@link EnforcingAdmin} for the caller.
+     *
+     * @param admin the component name of the admin.
+     * @param callerPackageName The package name  of the calling application.
+     * @param permission The name of the permission being checked.
+     * @param deviceAdminPolicy The userId of the user which the caller needs permission to act on.
+     * @throws SecurityException if the caller has not been granted the given permission,
+     * the associated cross-user permission if the caller's user is different to the target user.
+     */
+    private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin,
+            String permission, int deviceAdminPolicy, String callerPackageName, int targetUserId) {
+        enforcePermission(permission, deviceAdminPolicy, callerPackageName, targetUserId);
+        return getEnforcingAdminForCaller(admin, callerPackageName);
+    }
+
+    /**
      * Checks whether the calling process has been granted permission to query a device policy on
      * a specific user.
      * The given permission will be checked along with its associated cross-user permission if it
@@ -22748,6 +22826,9 @@
         POLICY_IDENTIFIER_TO_PERMISSION.put(AUTO_TIMEZONE_POLICY, SET_TIME_ZONE);
     }
 
+    private static final HashMap<String, Integer> POLICY_IDENTIFIER_TO_ACTIVE_ADMIN_POLICY =
+            new HashMap<>();
+
     /**
      * Checks if the calling process has been granted permission to apply a device policy on a
      * specific user.
@@ -22763,6 +22844,36 @@
     private void enforcePermission(String permission, String callerPackageName, int targetUserId)
             throws SecurityException {
         if (!hasPermission(permission, callerPackageName, targetUserId)) {
+            // TODO(b/276920002): Split the error messages so that the cross-user permission
+            // is only mentioned when it is needed.
+            throw new SecurityException("Caller does not have the required permissions for "
+                    + "this user. Permissions required: {"
+                    + permission
+                    + ", "
+                    + CROSS_USER_PERMISSIONS.get(permission)
+                    + "(if calling cross-user)"
+                    + "}");
+        }
+    }
+
+    /**
+     * Checks if the calling process has been granted permission to apply a device policy on a
+     * specific user.
+     * The given permission will be checked along with its associated cross-user permission if it
+     * exists and the target user is different to the calling user.
+     *
+     * @param callerPackageName The package name  of the calling application.
+     * @param permission The name of the permission being checked.
+     * @param targetUserId The userId of the user which the caller needs permission to act on.
+     * @throws SecurityException if the caller has not been granted the given permission,
+     * the associated cross-user permission if the caller's user is different to the target user.
+     */
+    private void enforcePermission(String permission, int adminPolicy,
+            String callerPackageName, int targetUserId)
+            throws SecurityException {
+        if (!hasPermissionOrAdminPolicy(permission, callerPackageName, adminPolicy, targetUserId)) {
+            // TODO(b/276920002): Split the error messages so that the cross-user permission
+            // is only mentioned when it is needed.
             throw new SecurityException("Caller does not have the required permissions for "
                     + "this user. Permissions required: {"
                     + permission
@@ -22804,16 +22915,26 @@
      */
     private boolean hasPermission(String permission, String callerPackageName, int targetUserId) {
         CallerIdentity caller = getCallerIdentity(callerPackageName);
-        boolean hasPermissionOnOwnUser = hasPermission(permission, callerPackageName);
+        boolean hasPermissionOnOwnUser = hasPermission(permission, caller.getPackageName());
         boolean hasPermissionOnTargetUser = true;
         if (hasPermissionOnOwnUser & caller.getUserId() != targetUserId) {
             hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission),
-                    callerPackageName);
+                    caller.getPackageName());
         }
 
         return hasPermissionOnOwnUser && hasPermissionOnTargetUser;
     }
 
+    private boolean hasPermissionOrAdminPolicy(String permission, String callerPackageName,
+            int adminPolicy, int targetUserId) {
+        CallerIdentity caller = getCallerIdentity(callerPackageName);
+        if (hasPermission(permission, caller.getPackageName(), targetUserId)) {
+            return true;
+        }
+        ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller);
+        return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy);
+    }
+
     /**
      * Return whether the calling process has been granted the given permission.
      *
@@ -22856,23 +22977,6 @@
         if (DELEGATE_SCOPES.containsKey(permission)) {
             return isCallerDelegate(caller, DELEGATE_SCOPES.get(permission));
         }
-        // Check if the caller is an active admin that uses a certain policy.
-        if (ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
-            try {
-                if (ACTIVE_ADMIN_POLICIES.get(permission) != null) {
-                    return getActiveAdminForCallerLocked(
-                            null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null;
-                } else {
-                    // If the permission maps to no policy (null) this means that any active admin
-                    // has permission.
-                    return isCallerActiveAdminOrDelegate(caller, null);
-                }
-            } catch (SecurityException e) {
-                // A security exception means there is not an active admin with permission and
-                // therefore
-                return false;
-            }
-        }
         return false;
     }
 
@@ -22925,9 +23029,7 @@
         if (admin != null) {
             return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
         }
-        if (admin == null) {
-            admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
-        }
+        admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
         return  EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
     }
 
@@ -23476,15 +23578,13 @@
     // We need to add a mapping of policyId to permission in POLICY_IDENTIFIER_TO_PERMISSION
     // for each migrated permission.
     private List<ActiveAdmin> getNonDPCActiveAdminsForPolicyLocked(String policyIdentifier) {
-        String permission = POLICY_IDENTIFIER_TO_PERMISSION.get(policyIdentifier);
-        if (permission == null) {
-            Slogf.e(LOG_TAG, "Can't find a permission for %s in POLICY_IDENTIFIER_TO_PERMISSION",
+        Integer activeAdminPolicy = POLICY_IDENTIFIER_TO_ACTIVE_ADMIN_POLICY.get(policyIdentifier);
+        if (activeAdminPolicy == null) {
+            Slogf.e(LOG_TAG,
+                    "Can't find a active admin policy for %s in POLICY_IDENTIFIER_TO_PERMISSION",
                     policyIdentifier);
             return new ArrayList<>();
         }
-        if (!ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
-            return new ArrayList<>();
-        }
 
         List<ActiveAdmin> admins = new ArrayList<>();
         for (UserInfo userInfo : mUserManager.getUsers()) {
@@ -23495,7 +23595,7 @@
                 }
                 DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
                 if (isActiveAdminWithPolicyForUserLocked(
-                        policy.mAdminMap.get(admin), ACTIVE_ADMIN_POLICIES.get(permission),
+                        policy.mAdminMap.get(admin), activeAdminPolicy,
                         userInfo.id)) {
                     admins.add(policy.mAdminMap.get(admin));
                 }
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index fc25152..3bef413 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -126,7 +126,7 @@
                         return mClock::now;
                     }
 
-                }, // pass in test looper instead, pass in offsetable clock
+                }, // pass in test looper instead, pass in offsettable clock
                 () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
                 mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
@@ -300,6 +300,179 @@
     }
 
     @Test
+    public void testShortTermModelTimesOut() throws Exception {
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Sensor reads 123 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+        // User sets brightness to 100
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+                /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
+                /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT,
+                /* shouldResetShortTermModel= */ true);
+
+        when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+        when(mBrightnessMappingStrategy.shouldResetShortTermModel(
+                123f, 0.5f)).thenReturn(true);
+
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+        mTestLooper.moveTimeForward(
+                mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
+        mTestLooper.dispatchAll();
+
+        mController.switchToInteractiveScreenBrightnessMode();
+        mTestLooper.moveTimeForward(4000);
+        mTestLooper.dispatchAll();
+
+        // Verify only happens on the first configure. (i.e. not again when switching back)
+        // Intentionally using any() to ensure it's not called whatsoever.
+        verify(mBrightnessMappingStrategy, times(1))
+                .addUserDataPoint(123.0f, 0.5f);
+        verify(mBrightnessMappingStrategy, times(1))
+                .addUserDataPoint(anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void testShortTermModelDoesntTimeOut() throws Exception {
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Sensor reads 123 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+        // User sets brightness to 100
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
+                0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
+                false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
+                /* shouldResetShortTermModel= */ true);
+
+        when(mBrightnessMappingStrategy.shouldResetShortTermModel(
+                anyFloat(), anyFloat())).thenReturn(true);
+        when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
+        when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.51f);
+        when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123.0f);
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+        // Time does not move forward, since clock is doesn't increment naturally.
+        mTestLooper.dispatchAll();
+
+        // Sensor reads 100000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910));
+        mController.switchToInteractiveScreenBrightnessMode();
+
+        // Verify short term model is not reset.
+        verify(mBrightnessMappingStrategy, never()).clearUserDataPoints();
+
+        // Verify that we add the data point once when the user sets it, and again when we return
+        // interactive mode.
+        verify(mBrightnessMappingStrategy, times(2))
+                .addUserDataPoint(123.0f, 0.51f);
+    }
+
+    @Test
+    public void testShortTermModelIsRestoredWhenSwitchingWithinTimeout() throws Exception {
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Sensor reads 123 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+        // User sets brightness to 100
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+                /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
+                /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT,
+                /* shouldResetShortTermModel= */ true);
+
+        when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
+        when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
+        when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f);
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+        when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f);
+        when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f);
+        when(mBrightnessMappingStrategy.shouldResetShortTermModel(
+                123f, 0.5f)).thenReturn(true);
+
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+        mTestLooper.moveTimeForward(
+                mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
+        mTestLooper.dispatchAll();
+
+        mController.switchToInteractiveScreenBrightnessMode();
+        mTestLooper.moveTimeForward(4000);
+        mTestLooper.dispatchAll();
+
+        // Verify only happens on the first configure. (i.e. not again when switching back)
+        // Intentionally using any() to ensure it's not called whatsoever.
+        verify(mBrightnessMappingStrategy, times(1))
+                .addUserDataPoint(123.0f, 0.5f);
+        verify(mBrightnessMappingStrategy, times(1))
+                .addUserDataPoint(anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void testShortTermModelNotRestoredAfterTimeout() throws Exception {
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Sensor reads 123 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+        // User sets brightness to 100
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+                /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
+                /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT,
+                /* shouldResetShortTermModel= */ true);
+
+        when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
+
+        when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
+        when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f);
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+        when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f);
+        when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f);
+
+        when(mBrightnessMappingStrategy.shouldResetShortTermModel(
+                123f, 0.5f)).thenReturn(true);
+
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+        // Do not fast-forward time.
+        mTestLooper.dispatchAll();
+
+        mController.switchToInteractiveScreenBrightnessMode();
+        // Do not fast-forward time
+        mTestLooper.dispatchAll();
+
+        // Verify this happens on the first configure and again when switching back
+        // Intentionally using any() to ensure it's not called any other times whatsoever.
+        verify(mBrightnessMappingStrategy, times(2))
+                .addUserDataPoint(123.0f, 0.5f);
+        verify(mBrightnessMappingStrategy, times(2))
+                .addUserDataPoint(anyFloat(), anyFloat());
+    }
+
+    @Test
     public void testSwitchToIdleMappingStrategy() throws Exception {
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
@@ -326,6 +499,11 @@
         // Called once for init, and once when switching,
         // setAmbientLux() is called twice and once in updateAutoBrightness()
         verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
+        // Called when switching.
+        verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
+        verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
+        verify(mBrightnessMappingStrategy, times(1)).getUserLux();
+
         // Ensure, after switching, original BMS is not used anymore
         verifyNoMoreInteractions(mBrightnessMappingStrategy);
 
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index ffe2fec..ce4b438 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -54,6 +54,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 @SmallTest
@@ -122,8 +123,6 @@
         BrightnessThrottlingData data;
         data = BrightnessThrottlingData.create((List<ThrottlingLevel>)null);
         assertEquals(data, null);
-        data = BrightnessThrottlingData.create((BrightnessThrottlingData)null);
-        assertEquals(data, null);
         data = BrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>());
         assertEquals(data, null);
         data = BrightnessThrottlingData.create(unsortedThermalLevels);
@@ -146,7 +145,7 @@
     }
 
     @Test
-    public void testThrottlingUnsupported() throws Exception {
+    public void testThrottlingUnsupported() {
         final BrightnessThrottler throttler = createThrottlerUnsupported();
         assertFalse(throttler.deviceSupportsThrottling());
 
@@ -307,37 +306,18 @@
         verify(mThermalServiceMock).registerThermalEventListenerWithType(
                 mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
         final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.4f);
 
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-
-        // Set status high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(0.4f, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-
-        // Update thresholds
-        // This data is equivalent to the string "123,1,critical,0.8", passed below
-        final ThrottlingLevel newLevel = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                0.8f);
         // Set new (valid) data from device config
         mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,0.8");
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f);
 
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-
-        // Set status high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(newLevel.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
+        mDeviceConfigFake.setBrightnessThrottlingData(
+                "123,1,critical,0.75;123,1,critical,0.99,id_2");
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.75f);
+        mDeviceConfigFake.setBrightnessThrottlingData(
+                "123,1,critical,0.8,default;123,1,critical,0.99,id_2");
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f);
     }
 
     @Test public void testInvalidThrottlingStrings() throws Exception {
@@ -370,6 +350,18 @@
         testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
         mDeviceConfigFake.setBrightnessThrottlingData("");                    // Invalid format
         testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+        // Invalid string format
+        mDeviceConfigFake.setBrightnessThrottlingData(
+                "123,default,1,critical,0.75,1,critical,0.99");
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+        // Invalid level string and number string
+        mDeviceConfigFake.setBrightnessThrottlingData(
+                "123,1,1,critical,0.75,id_2,1,critical,0.99");
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+        // Invalid format - (two default ids for same display)
+        mDeviceConfigFake.setBrightnessThrottlingData(
+                "123,1,critical,0.75,default;123,1,critical,0.99");
+        testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
     }
 
     private void testThrottling(BrightnessThrottler throttler, IThermalEventListener listener,
@@ -472,13 +464,17 @@
     }
 
     private BrightnessThrottler createThrottlerUnsupported() {
-        return new BrightnessThrottler(mInjectorMock, mHandler, mHandler, null, () -> {}, null);
+        return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
+                /* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
+                /* throttlingDataId= */ null, /* throttlingDataMap= */ new HashMap<>(1));
     }
 
     private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) {
         assertNotNull(data);
+        HashMap<String, BrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
+        throttlingDataMap.put("default", data);
         return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
-                data, () -> {}, "123");
+                () -> {}, "123", "default", throttlingDataMap);
     }
 
     private Temperature getSkinTemp(@ThrottlingStatus int status) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 9fd647b..ed07559 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -50,6 +50,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 @SmallTest
@@ -186,50 +187,72 @@
         assertArrayEquals(new int[]{-1, 10, 20, 30, 40},
                 mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
 
-        List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
-                new ArrayList();
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel>
+                defaultThrottlingLevels = new ArrayList<>();
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.4f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.3f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.2f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.1f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.05f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        defaultThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.025f
         ));
-        assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
-                mDisplayDeviceConfig.getBrightnessThrottlingData("default"));
 
-        throttlingLevels.clear();
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        DisplayDeviceConfig.BrightnessThrottlingData defaultThrottlingData =
+                new DisplayDeviceConfig.BrightnessThrottlingData(defaultThrottlingLevels);
+
+        List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel>
+                concurrentThrottlingLevels = new ArrayList<>();
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.2f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.15f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.1f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.05f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.025f
         ));
-        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+        concurrentThrottlingLevels.add(
+                new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
                 DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.0125f
         ));
-        assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
-                mDisplayDeviceConfig.getBrightnessThrottlingData("concurrent"));
+        DisplayDeviceConfig.BrightnessThrottlingData concurrentThrottlingData =
+                new DisplayDeviceConfig.BrightnessThrottlingData(concurrentThrottlingLevels);
+
+        HashMap<String, DisplayDeviceConfig.BrightnessThrottlingData> throttlingDataMap =
+                new HashMap<>(2);
+        throttlingDataMap.put("default", defaultThrottlingData);
+        throttlingDataMap.put("concurrent", concurrentThrottlingData);
+
+        assertEquals(throttlingDataMap,
+                mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId());
 
         assertNotNull(mDisplayDeviceConfig.getHostUsiVersion());
         assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMajorVersion(), 2);
@@ -246,8 +269,7 @@
                 mDisplayDeviceConfig.getHdrBrightnessFromSdr(0.62f, 1.25f),
                 SMALL_DELTA);
 
-
-        // Todo: Add asserts for BrightnessThrottlingData, DensityMapping,
+        // Todo: Add asserts for DensityMapping,
         // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
     }
 
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 dc954a2..1ba6ad7 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4734,7 +4734,7 @@
     }
 
     @Test
-    public void testBumpFGImportance_noChannelChangePreOApp() throws Exception {
+    public void testBumpFGImportance_channelChangePreOApp() throws Exception {
         String preOPkg = PKG_N_MR1;
         final ApplicationInfo legacy = new ApplicationInfo();
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -4752,7 +4752,7 @@
                 .setPriority(Notification.PRIORITY_MIN);
 
         StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
-                "testBumpFGImportance_noChannelChangePreOApp",
+                "testBumpFGImportance_channelChangePreOApp",
                 Binder.getCallingUid(), 0, nb.build(),
                 UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
 
@@ -4772,11 +4772,11 @@
                 .setPriority(Notification.PRIORITY_MIN);
 
         sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
-                "testBumpFGImportance_noChannelChangePreOApp", Binder.getCallingUid(),
+                "testBumpFGImportance_channelChangePreOApp", Binder.getCallingUid(),
                 0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
 
         mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg,
-                "testBumpFGImportance_noChannelChangePreOApp",
+                "testBumpFGImportance_channelChangePreOApp",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(IMPORTANCE_LOW,
@@ -4784,7 +4784,7 @@
 
         NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
                 preOPkg, mContext.getUserId(), preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
-        assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance());
+        assertEquals(IMPORTANCE_LOW, defaultChannel.getImportance());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 893f538..3ba9400 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -120,12 +120,20 @@
                 .showLights(false)
                 .showBadges(false)
                 .showInAmbientDisplay(false)
+                .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+                .allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED)
+                .allowConversations(ZenPolicy.CONVERSATION_SENDERS_NONE)
                 .build();
 
         ZenModeConfig config = getMutedAllConfig();
         config.allowAlarms = true;
         config.allowReminders = true;
         config.allowEvents = true;
+        config.allowCalls = true;
+        config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS;
+        config.allowMessages = true;
+        config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED;
+        config.allowConversations = false;
         config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
         config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
         config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -138,6 +146,10 @@
         assertEquals(expected.getPriorityCategoryEvents(), actual.getPriorityCategoryEvents());
         assertEquals(expected.getVisualEffectLights(), actual.getVisualEffectLights());
         assertEquals(expected.getVisualEffectAmbient(), actual.getVisualEffectAmbient());
+        assertEquals(expected.getPriorityConversationSenders(),
+                actual.getPriorityConversationSenders());
+        assertEquals(expected.getPriorityCallSenders(), actual.getPriorityCallSenders());
+        assertEquals(expected.getPriorityMessageSenders(), actual.getPriorityMessageSenders());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 12e4825..a15ee69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -41,6 +41,7 @@
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -146,7 +147,7 @@
         mActivity = setUpActivityWithComponent();
         mController = new LetterboxUiController(mWm, mActivity);
         prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
-        mController.setRelauchingAfterRequestedOrientationChanged(false);
+        mController.setRelaunchingAfterRequestedOrientationChanged(false);
 
         spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
         doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
@@ -190,6 +191,8 @@
 
     @Test
     public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() {
+        doReturn(true).when(mLetterboxConfiguration)
+                .isPolicyForIgnoringRequestedOrientationEnabled();
         doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
         // Request 3 times to simulate orientation request loop
         for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
@@ -200,8 +203,30 @@
 
     @Test
     @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+    public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse()
+            throws Exception {
+        doReturn(true).when(mLetterboxConfiguration)
+                .isPolicyForIgnoringRequestedOrientationEnabled();
+        mockThatProperty(PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
+                /* value */ false);
+        doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        // Request 3 times to simulate orientation request loop
+        for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
+            assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
+                    /* expectedCount */ 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
     public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() {
+        doReturn(true).when(mLetterboxConfiguration)
+                .isPolicyForIgnoringRequestedOrientationEnabled();
         doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+
         // Request 3 times to simulate orientation request loop
         for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
             assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
@@ -212,7 +237,10 @@
     @Test
     @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
     public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() {
+        doReturn(true).when(mLetterboxConfiguration)
+                .isPolicyForIgnoringRequestedOrientationEnabled();
         doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+
         // No orientation request loop
         assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
                 /* expectedCount */ 0);
@@ -222,7 +250,10 @@
     @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
     public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse()
             throws InterruptedException {
+        doReturn(true).when(mLetterboxConfiguration)
+                .isPolicyForIgnoringRequestedOrientationEnabled();
         doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+
         for (int i = MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i > 0; i--) {
             assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
                     /* expectedCount */ 0);
@@ -233,7 +264,10 @@
     @Test
     @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
     public void testShouldIgnoreOrientationRequestLoop_returnsTrue() {
+        doReturn(true).when(mLetterboxConfiguration)
+                .isPolicyForIgnoringRequestedOrientationEnabled();
         doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+
         for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
             assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
                     /* expectedCount */ i);
@@ -870,7 +904,7 @@
     private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() {
         doReturn(true).when(mLetterboxConfiguration)
                 .isPolicyForIgnoringRequestedOrientationEnabled();
-        mController.setRelauchingAfterRequestedOrientationChanged(true);
+        mController.setRelaunchingAfterRequestedOrientationChanged(true);
     }
 
     private ActivityRecord setUpActivityWithComponent() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a646d01..e96d1ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -813,6 +813,8 @@
 
         spyOn(mActivity.mLetterboxUiController);
         doReturn(true).when(mActivity.mLetterboxUiController)
+                .isSurfaceReadyToShow(any());
+        doReturn(true).when(mActivity.mLetterboxUiController)
                 .isSurfaceVisible(any());
 
         assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(