Merge "Move RingBuffer to SystemUICommon." into udc-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ffbfe82..2dfda51 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2277,12 +2277,12 @@
     method public int getMainDisplayIdAssignedToUser();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
+    method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isUserTypeEnabled(@NonNull String);
     method public boolean isVisibleBackgroundUsersOnDefaultDisplaySupported();
     method public boolean isVisibleBackgroundUsersSupported();
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
+    method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
   }
 
   public final class VibrationAttributes implements android.os.Parcelable {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 9bf56b3..99a7fa2 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -841,7 +841,8 @@
     /**
      * Perform the operation associated with this PendingIntent.
      *
-     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String,
+     *          Bundle)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
@@ -855,7 +856,8 @@
      *
      * @param code Result code to supply back to the PendingIntent's target.
      *
-     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String,
+     *          Bundle)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
@@ -875,7 +877,8 @@
      * original Intent. If flag {@link #FLAG_IMMUTABLE} was set when this
      * pending intent was created, this argument will be ignored.
      *
-     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String,
+     *          Bundle)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
@@ -892,6 +895,11 @@
      * @param options Additional options the caller would like to provide to modify the
      * sending behavior.  May be built from an {@link ActivityOptions} to apply to an
      * activity start.
+     *
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String)
+     *
+     * @throws CanceledException Throws CanceledException if the PendingIntent
+     * is no longer allowing more intents to be sent through it.
      */
     public void send(@Nullable Bundle options) throws CanceledException {
         send(null, 0, null, null, null, null, options);
@@ -908,7 +916,8 @@
      * should happen.  If null, the callback will happen from the thread
      * pool of the process.
      *
-     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String,
+     *          Bundle)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
@@ -942,11 +951,8 @@
      * should happen.  If null, the callback will happen from the thread
      * pool of the process.
      *
-     * @see #send()
-     * @see #send(int)
-     * @see #send(Context, int, Intent)
-     * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
-     * @see #send(Context, int, Intent, OnFinished, Handler, String)
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String,
+     *          Bundle)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
@@ -985,11 +991,8 @@
      * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
      * If null, no permission is required.
      *
-     * @see #send()
-     * @see #send(int)
-     * @see #send(Context, int, Intent)
-     * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
-     * @see #send(Context, int, Intent, OnFinished, Handler)
+     * @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler, String,
+     *          Bundle)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
@@ -1032,12 +1035,6 @@
      * @param options Additional options the caller would like to provide to modify the sending
      * behavior.  May be built from an {@link ActivityOptions} to apply to an activity start.
      *
-     * @see #send()
-     * @see #send(int)
-     * @see #send(Context, int, Intent)
-     * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
-     * @see #send(Context, int, Intent, OnFinished, Handler)
-     *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
      */
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 81fc029..23ba336 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -261,14 +261,17 @@
     public boolean guestToRemove;
 
     /**
-     * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
+     * This is used to optimize the creation of a user, i.e. OEMs might choose to pre-create a
      * number of users at the first boot, so the actual creation later is faster.
      *
      * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
      * user operations (other than user creation per se).
      *
-     * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
-     * {@code false}.
+     * <p>Once the pre-created is used to create a "real" user later on, {@code preCreated} is set
+     * to {@code false}.
+     *
+     * <p><b>NOTE: Pre-created users are deprecated. This field remains to be able to recognize
+     * pre-created users in older versions, but will eventually be removed.
      */
     public boolean preCreated;
 
@@ -277,6 +280,9 @@
      * user.
      *
      * <p><b>NOTE: </b>only used for debugging purposes, it's not set when marshalled to a parcel.
+     *
+     * <p><b>NOTE: Pre-created users are deprecated. This field remains to be able to recognize
+     * pre-created users in older versions, but will eventually ve removed.
      */
     public boolean convertedFromPreCreated;
 
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index d2a6f03..ac65933 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -21,20 +21,12 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import android.system.ErrnoException;
-import android.system.Os;
 
 import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
 
 /**
  * File descriptor of an entry in the AssetManager.  This provides your own
@@ -211,26 +203,19 @@
      */
     public static class AutoCloseInputStream
             extends ParcelFileDescriptor.AutoCloseInputStream {
-        /** Size of current file. */
-        private long mTotalSize;
-        /** The absolute position of current file start point. */
-        private final long mFileOffset;
-        /** The relative position where input stream is against mFileOffset. */
-        private long mOffset;
-        private OffsetCorrectFileChannel mOffsetCorrectFileChannel;
+        private long mRemaining;
 
         public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
             super(fd.getParcelFileDescriptor());
-            mTotalSize = fd.getLength();
-            mFileOffset = fd.getStartOffset();
+            super.skip(fd.getStartOffset());
+            mRemaining = (int) fd.getLength();
         }
 
         @Override
         public int available() throws IOException {
-            long available = mTotalSize - mOffset;
-            return available >= 0
-                    ? (available < 0x7fffffff ? (int) available : 0x7fffffff)
-                    : 0;
+            return mRemaining >= 0
+                    ? (mRemaining < 0x7fffffff ? (int) mRemaining : 0x7fffffff)
+                    : super.available();
         }
 
         @Override
@@ -242,24 +227,15 @@
 
         @Override
         public int read(byte[] buffer, int offset, int count) throws IOException {
-            int available = available();
-            if (available <= 0) {
-                return -1;
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                if (count > mRemaining) count = (int) mRemaining;
+                int res = super.read(buffer, offset, count);
+                if (res >= 0) mRemaining -= res;
+                return res;
             }
 
-            if (count > available) count = available;
-            try {
-                int res = Os.pread(getFD(), buffer, offset, count, mFileOffset + mOffset);
-                // pread returns 0 at end of file, while java's InputStream interface requires -1
-                if (res == 0) res = -1;
-                if (res > 0) {
-                    mOffset += res;
-                    updateChannelPosition(mOffset + mFileOffset);
-                }
-                return res;
-            } catch (ErrnoException e) {
-                throw new IOException(e);
-            }
+            return super.read(buffer, offset, count);
         }
 
         @Override
@@ -269,185 +245,41 @@
 
         @Override
         public long skip(long count) throws IOException {
-            int available = available();
-            if (available <= 0) {
-                return -1;
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                if (count > mRemaining) count = mRemaining;
+                long res = super.skip(count);
+                if (res >= 0) mRemaining -= res;
+                return res;
             }
 
-            if (count > available) count = available;
-            mOffset += count;
-            updateChannelPosition(mOffset + mFileOffset);
-            return count;
+            return super.skip(count);
         }
 
         @Override
         public void mark(int readlimit) {
-            // Not supported.
-            return;
+            if (mRemaining >= 0) {
+                // Not supported.
+                return;
+            }
+            super.mark(readlimit);
         }
 
         @Override
         public boolean markSupported() {
-            return false;
+            if (mRemaining >= 0) {
+                return false;
+            }
+            return super.markSupported();
         }
 
         @Override
         public synchronized void reset() throws IOException {
-            // Not supported.
-            return;
-        }
-
-        @Override
-        public FileChannel getChannel() {
-            if (mOffsetCorrectFileChannel == null) {
-                mOffsetCorrectFileChannel = new OffsetCorrectFileChannel(super.getChannel());
+            if (mRemaining >= 0) {
+                // Not supported.
+                return;
             }
-            try {
-                updateChannelPosition(mOffset + mFileOffset);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-            return mOffsetCorrectFileChannel;
-        }
-
-        /**
-         * Update the position of mOffsetCorrectFileChannel only after it is constructed.
-         *
-         * @param newPosition The absolute position mOffsetCorrectFileChannel needs to be moved to.
-         */
-        private void updateChannelPosition(long newPosition) throws IOException {
-            if (mOffsetCorrectFileChannel != null) {
-                mOffsetCorrectFileChannel.position(newPosition);
-            }
-        }
-
-        /**
-         * A FileChannel wrapper that will update mOffset of the AutoCloseInputStream
-         * to correct position when using FileChannel to read. All occurrence of position
-         * should be using absolute solution and each override method just do Delegation
-         * besides additional check. All methods related to write mode have been disabled
-         * and will throw UnsupportedOperationException with customized message.
-         */
-        private class OffsetCorrectFileChannel extends FileChannel {
-            private final FileChannel mDelegate;
-            private static final String METHOD_NOT_SUPPORTED_MESSAGE =
-                    "This Method is not supported in AutoCloseInputStream FileChannel.";
-
-            OffsetCorrectFileChannel(FileChannel fc) {
-                mDelegate = fc;
-            }
-
-            @Override
-            public int read(ByteBuffer dst) throws IOException {
-                if (available() <= 0) return -1;
-                int bytesRead = mDelegate.read(dst);
-                if (bytesRead != -1) mOffset += bytesRead;
-                return bytesRead;
-            }
-
-            @Override
-            public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
-                if (available() <= 0) return -1;
-                if (mOffset + length > mTotalSize) {
-                    length = (int) (mTotalSize - mOffset);
-                }
-                long bytesRead = mDelegate.read(dsts, offset, length);
-                if (bytesRead != -1) mOffset += bytesRead;
-                return bytesRead;
-            }
-
-            @Override
-            /**The only read method that does not move channel position*/
-            public int read(ByteBuffer dst, long position) throws IOException {
-                if (position - mFileOffset > mTotalSize) return -1;
-                return mDelegate.read(dst, position);
-            }
-
-            @Override
-            public long position() throws IOException {
-                return mDelegate.position();
-            }
-
-            @Override
-            public FileChannel position(long newPosition) throws IOException {
-                mOffset = newPosition - mFileOffset;
-                return mDelegate.position(newPosition);
-            }
-
-            @Override
-            public long size() throws IOException {
-                return mTotalSize;
-            }
-
-            @Override
-            public long transferTo(long position, long count, WritableByteChannel target)
-                    throws IOException {
-                if (position - mFileOffset > mTotalSize) {
-                    return 0;
-                }
-                if (position - mFileOffset + count > mTotalSize) {
-                    count = mTotalSize - (position - mFileOffset);
-                }
-                return mDelegate.transferTo(position, count, target);
-            }
-
-            @Override
-            public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
-                if (position - mFileOffset > mTotalSize) {
-                    throw new IOException(
-                            "Cannot map to buffer because position exceed current file size.");
-                }
-                if (position - mFileOffset + size > mTotalSize) {
-                    size = mTotalSize - (position - mFileOffset);
-                }
-                return mDelegate.map(mode, position, size);
-            }
-
-            @Override
-            protected void implCloseChannel() throws IOException {
-                mDelegate.close();
-            }
-
-            @Override
-            public int write(ByteBuffer src) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public int write(ByteBuffer src, long position) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public long transferFrom(ReadableByteChannel src, long position, long count)
-                    throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public FileChannel truncate(long size) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public void force(boolean metaData) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public FileLock lock(long position, long size, boolean shared) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
-
-            @Override
-            public FileLock tryLock(long position, long size, boolean shared) throws IOException {
-                throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
-            }
+            super.reset();
         }
     }
 
diff --git a/core/java/android/hardware/biometrics/ComponentInfoInternal.java b/core/java/android/hardware/biometrics/ComponentInfoInternal.java
index 3b61a56..2e708de 100644
--- a/core/java/android/hardware/biometrics/ComponentInfoInternal.java
+++ b/core/java/android/hardware/biometrics/ComponentInfoInternal.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 
 /**
  * The internal class for storing the component info for a subsystem of the biometric sensor,
@@ -90,12 +92,19 @@
         dest.writeString(softwareVersion);
     }
 
-    @Override
-    public String toString() {
-        return "ComponentId: " + componentId
-                + ", HardwareVersion: " + hardwareVersion
-                + ", FirmwareVersion: " + firmwareVersion
-                + ", SerialNumber " + serialNumber
-                + ", SoftwareVersion: " + softwareVersion;
+    /**
+     * Print the component info into the given stream.
+     *
+     * @param pw The stream to dump the info into.
+     * @hide
+     */
+    public void dump(@NonNull IndentingPrintWriter pw) {
+        pw.println(TextUtils.formatSimple("componentId: %s", componentId));
+        pw.increaseIndent();
+        pw.println(TextUtils.formatSimple("hardwareVersion: %s", hardwareVersion));
+        pw.println(TextUtils.formatSimple("firmwareVersion: %s", firmwareVersion));
+        pw.println(TextUtils.formatSimple("serialNumber: %s", serialNumber));
+        pw.println(TextUtils.formatSimple("softwareVersion: %s", softwareVersion));
+        pw.decreaseIndent();
     }
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index c88af5a..1a38c88 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -58,10 +58,10 @@
     boolean hasEnrolledBiometrics(int userId, String opPackageName);
 
     // Registers an authenticator (e.g. face, fingerprint, iris).
-    // Id must be unique, whereas strength and modality don't need to be.
+    // Sensor Id in sensor props must be unique, whereas modality doesn't need to be.
     // TODO(b/123321528): Turn strength and modality into enums.
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
-    void registerAuthenticator(int id, int modality, int strength,
+    void registerAuthenticator(int modality, in SensorPropertiesInternal props,
             IBiometricAuthenticator authenticator);
 
     // Register callback for when keyguard biometric eligibility changes.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b3604da..24e28e9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3706,17 +3706,24 @@
      * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
      * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION.
      *
+     *
      * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
      * @return the {@link UserInfo} object for the created user.
      *
      * @throws UserOperationException if the user could not be created.
+     *
+     * @deprecated Pre-created users are deprecated. This method should no longer be used, and will
+     *             be removed once all the callers are removed.
+     *
      * @hide
      */
+    @Deprecated
     @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
     public @NonNull UserInfo preCreateUser(@NonNull String userType)
             throws UserOperationException {
+        Log.w(TAG, "preCreateUser(): Pre-created user is deprecated.");
         try {
             return mService.preCreateUserWithThrow(userType);
         } catch (ServiceSpecificException e) {
@@ -4296,8 +4303,12 @@
     /**
      * Returns information for all users on this device, based on the filtering parameters.
      *
+     * @deprecated Pre-created users are deprecated and no longer supported.
+     *             Use {@link #getUsers()}, {@link #getUsers(boolean)}, or {@link #getAliveUsers()}
+     *             instead.
      * @hide
      */
+    @Deprecated
     @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_USERS,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c473d3f..7cb959d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3431,7 +3431,7 @@
                                     + " type:" + mUri.getPath()
                                     + " in package:" + cr.getPackageName());
                         }
-                        for (int i = 0; i < mValues.size(); ++i) {
+                        for (int i = mValues.size() - 1; i >= 0; i--) {
                             String key = mValues.keyAt(i);
                             if (key.startsWith(prefix)) {
                                 mValues.remove(key);
@@ -18125,12 +18125,6 @@
             public static final String WEAR_OS_VERSION_STRING = "wear_os_version_string";
 
             /**
-             * Whether the physical button has been set.
-             * @hide
-             */
-            public static final String BUTTON_SET = "button_set";
-
-            /**
              * Whether there is a side button.
              * @hide
              */
@@ -18302,6 +18296,12 @@
             public static final int COMPANION_OS_VERSION_UNDEFINED = -1;
 
             /**
+             * The companion App name.
+             * @hide
+             */
+            public static final String COMPANION_APP_NAME = "wear_companion_app_name";
+
+            /**
              * A boolean value to indicate if we want to support all languages in LE edition on
              * wear. 1 for supporting, 0 for not supporting.
              * @hide
@@ -18413,10 +18413,13 @@
             public static final String BURN_IN_PROTECTION_ENABLED = "burn_in_protection";
 
             /**
-
              * Whether the device has combined location setting enabled.
+             *
+             * @deprecated Use LocationManager as the source of truth for all location states.
+             *
              * @hide
              */
+            @Deprecated
             public static final String COMBINED_LOCATION_ENABLED = "combined_location_enable";
 
             /**
@@ -18482,67 +18485,36 @@
             public static final String CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED =
                     "clockwork_long_press_to_assistant_enabled";
 
-            /*
+            /**
              * Whether the device has Cooldown Mode enabled.
              * @hide
              */
             public static final String COOLDOWN_MODE_ON = "cooldown_mode_on";
 
-            /*
+            /**
              * Whether the device has Wet Mode/ Touch Lock Mode enabled.
              * @hide
              */
             public static final String WET_MODE_ON = "wet_mode_on";
 
-            /*
+            /**
              * Whether the RSB wake feature is enabled.
              * @hide
              */
             public static final String RSB_WAKE_ENABLED = "rsb_wake_enabled";
 
-            /*
+            /**
              * Whether the screen-unlock (keyguard) sound is enabled.
              * @hide
              */
             public static final String SCREEN_UNLOCK_SOUND_ENABLED = "screen_unlock_sound_enabled";
 
-            /*
+            /**
              * Whether charging sounds are enabled.
              * @hide
              */
             public static final String CHARGING_SOUNDS_ENABLED = "wear_charging_sounds_enabled";
 
-            /** The status of the early updates process.
-             * @hide
-             */
-            public static final String EARLY_UPDATES_STATUS = "early_updates_status";
-
-            /**
-             * Early updates not started
-             * @hide
-             */
-            public static final int EARLY_UPDATES_STATUS_NOT_STARTED = 0;
-            /**
-             * Early updates started and in progress
-             * @hide
-             */
-            public static final int EARLY_UPDATES_STATUS_STARTED = 1;
-            /**
-             * Early updates completed and was successful
-             * @hide
-             */
-            public static final int EARLY_UPDATES_STATUS_SUCCESS = 2;
-            /**
-             * Early updates skipped
-             * @hide
-             */
-            public static final int EARLY_UPDATES_STATUS_SKIPPED = 3;
-            /**
-             * Early updates aborted due to timeout
-             * @hide
-             */
-            public static final int EARLY_UPDATES_STATUS_ABORTED = 4;
-
             /**
              * Whether dynamic color theming (e.g. Material You) is enabled for apps which support
              * it.
@@ -18669,6 +18641,174 @@
              * @hide
              */
             public static final int UPGRADE_DATA_MIGRATION_DONE = 2;
+
+            /**
+             * Whether to disable AOD while plugged.
+             * (0 = false, 1 = true)
+             * @hide
+             */
+            public static final String DISABLE_AOD_WHILE_PLUGGED = "disable_aod_while_plugged";
+
+            /**
+             * Whether the user has consented for network location provider (NLP).
+             * This setting key will only be used once during OOBE to set NLP initial value through
+             * the companion app ToS. This setting key will be synced over from Companion and
+             * corresponding toggle in GMS will be enabled.
+             * @hide
+             */
+            public static final String NETWORK_LOCATION_OPT_IN = "network_location_opt_in";
+
+            /**
+             * The custom foreground color.
+             * @hide
+             */
+            public static final String CUSTOM_COLOR_FOREGROUND = "custom_foreground_color";
+
+            /**
+             * The custom background color.
+             * @hide
+             */
+            public static final String CUSTOM_COLOR_BACKGROUND = "custom_background_color";
+
+            /** The status of the phone switching process.
+             * @hide
+             */
+            public static final String PHONE_SWITCHING_STATUS = "phone_switching_status";
+
+            /**
+             * Phone switching not started
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_NOT_STARTED = 0;
+
+            /**
+             * Phone switching started
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_STARTED = 1;
+
+            /**
+             * Phone switching completed and was successful
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_SUCCESS = 2;
+
+            /**
+             * Phone switching was cancelled
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_CANCELLED = 3;
+
+            /**
+             * Phone switching failed
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_FAILED = 4;
+
+            /**
+             * Phone switching is in progress of advertising to new companion device.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_ADVERTISING = 5;
+
+            /**
+             * Phone switching successfully bonded with new companion device.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_BONDED = 6;
+
+            /**
+             * Phone switching successfully completed on phone side.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_PHONE_COMPLETE = 7;
+
+            /**
+             * Connection config migration in progress.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_MIGRATION = 8;
+
+            /**
+             * Connection config migration failed.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_MIGRATION_FAILED = 9;
+
+            /**
+             * Connection config migration cancellation in progress.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_MIGRATION_CANCELLED = 10;
+
+            /**
+             * Connection config migration success.
+             * @hide
+             */
+            public static final int PHONE_SWITCHING_STATUS_IN_PROGRESS_MIGRATION_SUCCESS = 11;
+
+
+            /**
+             * Whether the device has enabled the feature to reduce motion and animation
+             * (0 = false, 1 = true)
+             * @hide
+             */
+            public static final String REDUCE_MOTION = "reduce_motion";
+
+            /**
+             * Whether RTL swipe-to-dismiss is enabled by developer options.
+             * (0 = false, 1 = true)
+             * @hide
+             */
+            public static final String RTL_SWIPE_TO_DISMISS_ENABLED_DEV =
+                    "rtl_swipe_to_dismiss_enabled_dev";
+
+            /**
+             * Tethered Configuration state.
+             * @hide
+             */
+            public static final String TETHER_CONFIG_STATE = "tethered_config_state";
+
+            /**
+             * Tethered configuration state is unknown.
+             * @hide
+             */
+            public static final int TETHERED_CONFIG_UNKNOWN = 0;
+
+            /**
+             * Device is set into standalone mode.
+             * @hide
+             */
+            public static final int TETHERED_CONFIG_STANDALONE = 1;
+
+            /**
+             * Device is set in tethered mode.
+             * @hide
+             */
+            public static final int TETHERED_CONFIG_TETHERED = 2;
+
+
+            /**
+             * Whether phone switching is supported.
+             *
+             * (0 = false, 1 = true)
+             * @hide
+             */
+            public static final String PHONE_SWITCHING_SUPPORTED = "phone_switching_supported";
+
+            /**
+             * Setting indicating the name of the Wear OS package that hosts the Media Controls UI.
+             *
+             * @hide
+             */
+            public static final String WEAR_MEDIA_CONTROLS_PACKAGE = "wear_media_controls_package";
+
+            /**
+             * Setting indicating the name of the Wear OS package responsible for bridging media.
+             *
+             * @hide
+             */
+            public static final String WEAR_MEDIA_SESSIONS_PACKAGE = "wear_media_sessions_package";
         }
     }
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3ff6351..e4d74b5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6067,6 +6067,12 @@
     <!-- Wear OS: the name of the main activity of the device's sysui. -->
     <string name="config_wearSysUiMainActivity" translatable="false"/>
 
+    <!-- Wear OS: the name of the package containing the Media Controls Activity. -->
+    <string name="config_wearMediaControlsPackage" translatable="false"/>
+
+    <!-- Wear OS: the name of the package containing the Media Sessions APK. -->
+    <string name="config_wearMediaSessionsPackage" translatable="false"/>
+
     <bool name="config_secondaryBuiltInDisplayIsRound">@bool/config_windowIsRound</bool>
 
     <!-- The display round config for each display in a multi-display device. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7b582da..c6c1c8f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3138,7 +3138,6 @@
 
   <!--  Work profile unlaunchable app alert dialog-->
   <java-symbol type="style" name="AlertDialogWithEmergencyButton"/>
-  <java-symbol type="string" name="work_mode_dialer_off_message" />
   <java-symbol type="string" name="work_mode_emergency_call_button" />
   <java-symbol type="string" name="work_mode_off_title" />
   <java-symbol type="string" name="work_mode_off_message" />
@@ -4865,6 +4864,8 @@
 
   <java-symbol type="string" name="config_wearSysUiPackage"/>
   <java-symbol type="string" name="config_wearSysUiMainActivity"/>
+  <java-symbol type="string" name="config_wearMediaControlsPackage"/>
+  <java-symbol type="string" name="config_wearMediaSessionsPackage"/>
   <java-symbol type="string" name="config_defaultQrCodeComponent"/>
 
   <java-symbol type="dimen" name="secondary_rounded_corner_radius" />
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index 980211f..316c70c 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -25,7 +25,8 @@
 import android.app.Activity;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.os.Bundle;
-import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.IwTest;
+import android.platform.test.annotations.Postsubmit;
 import android.provider.Settings;
 import android.util.PollingCheck;
 import android.view.View;
@@ -59,7 +60,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-@Presubmit
+@Postsubmit
 public class FontScaleConverterActivityTest {
     @Rule
     public ActivityScenarioRule<TestActivity> rule = new ActivityScenarioRule<>(TestActivity.class);
@@ -84,6 +85,7 @@
         }
     }
 
+    @IwTest(focusArea = "accessibility")
     @Test
     public void testFontsScaleNonLinearly() {
         final ActivityScenario<TestActivity> scenario = rule.getScenario();
@@ -114,6 +116,7 @@
         )));
     }
 
+    @IwTest(focusArea = "accessibility")
     @Test
     public void testOnConfigurationChanged_doesNotCrash() {
         final ActivityScenario<TestActivity> scenario = rule.getScenario();
@@ -127,6 +130,7 @@
         });
     }
 
+    @IwTest(focusArea = "accessibility")
     @Test
     public void testUpdateConfiguration_doesNotCrash() {
         final ActivityScenario<TestActivity> scenario = rule.getScenario();
diff --git a/core/tests/coretests/src/android/content/res/TEST_MAPPING b/core/tests/coretests/src/android/content/res/TEST_MAPPING
index 4ea6e40..ab14950 100644
--- a/core/tests/coretests/src/android/content/res/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/res/TEST_MAPPING
@@ -39,5 +39,18 @@
         }
       ]
     }
+  ],
+  "ironwood-postsubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options":[
+        {
+            "include-annotation": "android.platform.test.annotations.IwTest"
+        },
+        {
+            "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
   ]
 }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index c106854..0eb4caa 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -475,6 +475,18 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/RecentTasks.java"
     },
+    "-1643780158": {
+      "message": "Saving original orientation before camera compat, last orientation is %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
+    },
+    "-1639406696": {
+      "message": "NOSENSOR override detected",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java"
+    },
     "-1638958146": {
       "message": "Removing activity %s from task=%s adding to task=%s Callers=%s",
       "level": "INFO",
@@ -751,6 +763,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
     },
+    "-1397175017": {
+      "message": "Other orientation overrides are in place: not reverting",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java"
+    },
     "-1394745488": {
       "message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
       "level": "INFO",
@@ -1303,6 +1321,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-874087484": {
+      "message": "SyncGroup %d: Set ready %b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    },
     "-869242375": {
       "message": "Content Recording: Unable to start recording due to invalid region for display %d",
       "level": "VERBOSE",
@@ -1705,6 +1729,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "-529187878": {
+      "message": "Reverting orientation after camera compat force rotation",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
+    },
     "-521613870": {
       "message": "App died during pause, not stopping: %s",
       "level": "VERBOSE",
@@ -2377,6 +2407,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "138097009": {
+      "message": "NOSENSOR override is absent: reverting",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java"
+    },
     "140319294": {
       "message": "IME target changed within ActivityRecord",
       "level": "DEBUG",
@@ -4009,12 +4045,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "1689989893": {
-      "message": "SyncGroup %d: Set ready",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
-    },
     "1699269281": {
       "message": "Don't organize or trigger events for untrusted displayId=%d",
       "level": "WARN",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index f998217..afc573e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -197,7 +197,7 @@
 
     @Override
     public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
-        if (mTransitionPausingRelayout.equals(merged)) {
+        if (merged.equals(mTransitionPausingRelayout)) {
             mTransitionPausingRelayout = playing;
         }
     }
@@ -312,8 +312,12 @@
             } else if (id == R.id.back_button) {
                 mTaskOperations.injectBackKey();
             } else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
-                moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
-                decoration.createHandleMenu();
+                if (!decoration.isHandleMenuActive()) {
+                    moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
+                    decoration.createHandleMenu();
+                } else {
+                    decoration.closeHandleMenu();
+                }
             } else if (id == R.id.desktop_button) {
                 mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
                 mDesktopTasksController.ifPresent(c -> c.moveToDesktop(mTaskId));
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 efc90b5..f9c0e60 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
@@ -487,6 +487,14 @@
         if (mHandleMenuAppInfoPill.mWindowViewHost.getView().getWidth() == 0) return;
 
         PointF inputPoint = offsetCaptionLocation(ev);
+
+        // If this is called before open_menu_button's onClick, we don't want to close
+        // the menu since it will just reopen in onClick.
+        final boolean pointInOpenMenuButton = pointInView(
+                mResult.mRootView.findViewById(R.id.open_menu_button),
+                inputPoint.x,
+                inputPoint.y);
+
         final boolean pointInAppInfoPill = pointInView(
                 mHandleMenuAppInfoPill.mWindowViewHost.getView(),
                 inputPoint.x - mHandleMenuAppInfoPillPosition.x - mResult.mDecorContainerOffsetX,
@@ -506,7 +514,8 @@
                         - mResult.mDecorContainerOffsetX,
                 inputPoint.y - mHandleMenuMoreActionsPillPosition.y
                         - mResult.mDecorContainerOffsetY);
-        if (!pointInAppInfoPill && !pointInWindowingPill && !pointInMoreActionsPill) {
+        if (!pointInAppInfoPill && !pointInWindowingPill
+                && !pointInMoreActionsPill && !pointInOpenMenuButton) {
             closeHandleMenu();
         }
     }
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index d08bc5c5..8049dc9 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -29,9 +29,10 @@
 
 namespace android {
 
-AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
-        : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
-    mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration());
+AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed,
+                                             SkEncodedImageFormat format)
+        : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed), mFormat(format) {
+    mTimeToShowNextSnapshot = ms2ns(currentFrameDuration());
     setStagingBounds(mSkAnimatedImage->getBounds());
 }
 
@@ -92,7 +93,7 @@
         // directly from mSkAnimatedImage.
         lock.unlock();
         std::unique_lock imageLock{mImageLock};
-        *outDelay = ms2ns(mSkAnimatedImage->currentFrameDuration());
+        *outDelay = ms2ns(currentFrameDuration());
         return true;
     } else {
         // The next snapshot has not yet been decoded, but we've already passed
@@ -109,7 +110,7 @@
     Snapshot snap;
     {
         std::unique_lock lock{mImageLock};
-        snap.mDurationMS = mSkAnimatedImage->decodeNextFrame();
+        snap.mDurationMS = adjustFrameDuration(mSkAnimatedImage->decodeNextFrame());
         snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
     }
 
@@ -123,7 +124,7 @@
         std::unique_lock lock{mImageLock};
         mSkAnimatedImage->reset();
         snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
-        snap.mDurationMS = mSkAnimatedImage->currentFrameDuration();
+        snap.mDurationMS = currentFrameDuration();
     }
 
     return snap;
@@ -274,7 +275,7 @@
         {
             std::unique_lock lock{mImageLock};
             mSkAnimatedImage->reset();
-            durationMS = mSkAnimatedImage->currentFrameDuration();
+            durationMS = currentFrameDuration();
         }
         {
             std::unique_lock lock{mSwapLock};
@@ -306,7 +307,7 @@
     {
         std::unique_lock lock{mImageLock};
         if (update) {
-            durationMS = mSkAnimatedImage->decodeNextFrame();
+            durationMS = adjustFrameDuration(mSkAnimatedImage->decodeNextFrame());
         }
 
         canvas->drawDrawable(mSkAnimatedImage.get());
@@ -336,4 +337,20 @@
     return SkRectMakeLargest();
 }
 
+int AnimatedImageDrawable::adjustFrameDuration(int durationMs) {
+    if (durationMs == SkAnimatedImage::kFinished) {
+        return SkAnimatedImage::kFinished;
+    }
+
+    if (mFormat == SkEncodedImageFormat::kGIF) {
+        // Match Chrome & Firefox behavior that gifs with a duration <= 10ms is bumped to 100ms
+        return durationMs <= 10 ? 100 : durationMs;
+    }
+    return durationMs;
+}
+
+int AnimatedImageDrawable::currentFrameDuration() {
+    return adjustFrameDuration(mSkAnimatedImage->currentFrameDuration());
+}
+
 }  // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 8ca3c7e..1e965ab 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -16,16 +16,16 @@
 
 #pragma once
 
-#include <cutils/compiler.h>
-#include <utils/Macros.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
 #include <SkAnimatedImage.h>
 #include <SkCanvas.h>
 #include <SkColorFilter.h>
 #include <SkDrawable.h>
+#include <SkEncodedImageFormat.h>
 #include <SkPicture.h>
+#include <cutils/compiler.h>
+#include <utils/Macros.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
 
 #include <future>
 #include <mutex>
@@ -48,7 +48,8 @@
 public:
     // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the
     // Snapshots.
-    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed);
+    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed,
+                          SkEncodedImageFormat format);
 
     /**
      * This updates the internal time and returns true if the image needs
@@ -115,6 +116,7 @@
 private:
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
     const size_t mBytesUsed;
+    const SkEncodedImageFormat mFormat;
 
     bool mRunning = false;
     bool mStarting = false;
@@ -157,6 +159,9 @@
     Properties mProperties;
 
     std::unique_ptr<OnAnimationEndListener> mEndListener;
+
+    int adjustFrameDuration(int);
+    int currentFrameDuration();
 };
 
 }  // namespace android
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index 373e893..a7f5aa83 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -97,7 +97,7 @@
         bytesUsed += picture->approximateBytesUsed();
     }
 
-
+    SkEncodedImageFormat format = imageDecoder->mCodec->getEncodedFormat();
     sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
                                                                info, subset,
                                                                std::move(picture));
@@ -108,8 +108,8 @@
 
     bytesUsed += sizeof(animatedImg.get());
 
-    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
-                                                                    bytesUsed));
+    sk_sp<AnimatedImageDrawable> drawable(
+            new AnimatedImageDrawable(std::move(animatedImg), bytesUsed, format));
     return reinterpret_cast<jlong>(drawable.release());
 }
 
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 01a93c7..cce5468e 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -53,7 +53,6 @@
     void onStop() override;
     bool isSurfaceReady() override;
     bool isContextReady() override;
-    bool supportsExtendedRangeHdr() const override { return true; }
     void setTargetSdrHdrRatio(float ratio) override;
     const SkM44& getPixelSnapMatrix() const override;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1b9d41a7..f60c1f3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -236,7 +236,6 @@
 
     if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) {
         setBufferCount(mNativeSurface->getNativeWindow());
-
     }
 
     mFrameNumber = 0;
@@ -301,10 +300,6 @@
 
 float CanvasContext::setColorMode(ColorMode mode) {
     if (mode != mColorMode) {
-        const bool isHdr = mode == ColorMode::Hdr || mode == ColorMode::Hdr10;
-        if (isHdr && !mRenderPipeline->supportsExtendedRangeHdr()) {
-            mode = ColorMode::WideColorGamut;
-        }
         mColorMode = mode;
         mRenderPipeline->setSurfaceColorProperties(mode);
         setupPipelineSurface();
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 4fb114b..94f35fd 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -423,6 +423,7 @@
     EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE};
 
     EGLConfig config = mEglConfig;
+    bool overrideWindowDataSpaceForHdr = false;
     if (colorMode == ColorMode::A8) {
         // A8 doesn't use a color space
         if (!mEglConfigA8) {
@@ -450,12 +451,13 @@
                 case ColorMode::Default:
                     attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
                     break;
-                // Extended Range HDR requires being able to manipulate the dataspace in ways
-                // we cannot easily do while going through EGLSurface. Given this requires
-                // composer3 support, just treat HDR as equivalent to wide color gamut if
-                // the GLES path is still being hit
+                // We don't have an EGL colorspace for extended range P3 that's used for HDR
+                // So override it after configuring the EGL context
                 case ColorMode::Hdr:
                 case ColorMode::Hdr10:
+                    overrideWindowDataSpaceForHdr = true;
+                    attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+                    break;
                 case ColorMode::WideColorGamut: {
                     skcms_Matrix3x3 colorGamut;
                     LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
@@ -491,6 +493,16 @@
                             (void*)window, eglErrorString());
     }
 
+    if (overrideWindowDataSpaceForHdr) {
+        // This relies on knowing that EGL will not re-set the dataspace after the call to
+        // eglCreateWindowSurface. Since the handling of the colorspace extension is largely
+        // implemented in libEGL in the platform, we can safely assume this is the case
+        int32_t err = ANativeWindow_setBuffersDataSpace(
+                window,
+                static_cast<android_dataspace>(STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_EXTENDED));
+        LOG_ALWAYS_FATAL_IF(err, "Failed to ANativeWindow_setBuffersDataSpace %d", err);
+    }
+
     return surface;
 }
 
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 9ebad81..6c2cb9d 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -95,7 +95,6 @@
     virtual void setPictureCapturedCallback(
             const std::function<void(sk_sp<SkPicture>&&)>& callback) = 0;
 
-    virtual bool supportsExtendedRangeHdr() const { return false; }
     virtual void setTargetSdrHdrRatio(float ratio) = 0;
     virtual const SkM44& getPixelSnapMatrix() const = 0;
 
diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h
index c753020..060abfd 100644
--- a/media/jni/android_media_MediaCodecLinearBlock.h
+++ b/media/jni/android_media_MediaCodecLinearBlock.h
@@ -44,12 +44,19 @@
 
     std::shared_ptr<C2Buffer> toC2Buffer(size_t offset, size_t size) const {
         if (mBuffer) {
+            // TODO: if returned C2Buffer is different from mBuffer, we should
+            // find a way to connect the life cycle between this C2Buffer and
+            // mBuffer.
             if (mBuffer->data().type() != C2BufferData::LINEAR) {
                 return nullptr;
             }
             C2ConstLinearBlock block = mBuffer->data().linearBlocks().front();
             if (offset == 0 && size == block.capacity()) {
-                return mBuffer;
+                // Let C2Buffer be new one to queue to MediaCodec. It will allow
+                // the related input slot to be released by onWorkDone from C2
+                // Component. Currently, the life cycle of mBuffer should be
+                // protected by different flows.
+                return std::make_shared<C2Buffer>(*mBuffer);
             }
 
             std::shared_ptr<C2Buffer> buffer =
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index d87abb9..ebfb86d 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -34,7 +34,7 @@
     <string name="summary_watch">This app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
+    <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_type" example="phone">%1$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_GLASSES ================= -->
 
@@ -48,7 +48,7 @@
     <string name="summary_glasses_multi_device">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
+    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your <xliff:g id="device_type" example="phone">%1$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
 
@@ -59,7 +59,7 @@
     <string name="helper_title_app_streaming">Cross-device services</string>
 
     <!-- Description of the helper dialog for APP_STREAMING profile. [CHAR LIMIT=NONE] -->
-    <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string>
+    <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="display_name" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string>
 
     <!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= -->
 
@@ -81,7 +81,7 @@
     <string name="helper_title_computer">Google Play services</string>
 
     <!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] -->
-    <string name="helper_summary_computer"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string>
+    <string name="helper_summary_computer"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="display_name" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string>
 
     <!-- ================= DEVICE_PROFILE_NEARBY_DEVICE_STREAMING ================= -->
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index dd4419b..e53e956 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -65,8 +65,8 @@
         )
 
         val originName: String? = when (requestInfo?.type) {
-            RequestInfo.TYPE_CREATE -> requestInfo?.createCredentialRequest?.origin
-            RequestInfo.TYPE_GET -> requestInfo?.getCredentialRequest?.origin
+            RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin
+            RequestInfo.TYPE_GET -> requestInfo.getCredentialRequest?.origin
             else -> null
         }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 725401f..ca89129 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -210,7 +210,11 @@
                 appName = originName
                     ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
                     ?: return null,
-                preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
+                preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
+                preferIdentityDocUi = getCredentialRequest.data.getBoolean(
+                    // TODO(b/276777444): replace with direct library constant reference once
+                    // exposed.
+                    "androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"),
             )
         }
 
@@ -241,7 +245,7 @@
                             userName = credentialEntry.username.toString(),
                             displayName = credentialEntry.displayName?.toString(),
                             icon = credentialEntry.icon.loadDrawable(context),
-                            shouldTintIcon = credentialEntry.isDefaultIcon ?: false,
+                            shouldTintIcon = credentialEntry.isDefaultIcon,
                             lastUsedTimeMillis = credentialEntry.lastUsedTime,
                             isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
                                 credentialEntry.autoSelectAllowedFromOption,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
index 307d953..10a75d4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
@@ -316,7 +316,7 @@
         rememberModalBottomSheetState(Hidden),
     sheetShape: Shape = MaterialTheme.shapes.large,
     sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
-    sheetBackgroundColor: Color = MaterialTheme.colorScheme.surface,
+    sheetBackgroundColor: Color,
     sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
     content: @Composable () -> Unit
 ) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 7a720b1..0623ff6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -32,7 +32,6 @@
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.SuggestionChip
 import androidx.compose.material3.SuggestionChipDefaults
 import androidx.compose.material3.TopAppBar
@@ -155,7 +154,7 @@
                             // Decorative purpose only.
                             contentDescription = null,
                             modifier = Modifier.size(24.dp),
-                            tint = MaterialTheme.colorScheme.onSurfaceVariant,
+                            tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
                         )
                     }
                 }
@@ -169,7 +168,7 @@
                         Icon(
                             modifier = iconSize,
                             bitmap = iconImageBitmap,
-                            tint = MaterialTheme.colorScheme.onSurfaceVariant,
+                            tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
                             // Decorative purpose only.
                             contentDescription = null,
                         )
@@ -193,7 +192,7 @@
                     Icon(
                         modifier = iconSize,
                         imageVector = iconImageVector,
-                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
+                        tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
                         // Decorative purpose only.
                         contentDescription = null,
                     )
@@ -205,7 +204,7 @@
                     Icon(
                         modifier = iconSize,
                         painter = iconPainter,
-                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
+                        tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
                         // Decorative purpose only.
                         contentDescription = null,
                     )
@@ -217,9 +216,8 @@
         border = null,
         colors = SuggestionChipDefaults.suggestionChipColors(
             containerColor = LocalAndroidColorScheme.current.colorSurfaceContainerHigh,
-            // TODO: remove?
-            labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
-            iconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+            labelColor = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+            iconContentColor = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
         ),
     )
 }
@@ -282,7 +280,7 @@
         Icon(
             modifier = Modifier.size(24.dp),
             painter = leadingIconPainter,
-            tint = MaterialTheme.colorScheme.onSurfaceVariant,
+            tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
             // Decorative purpose only.
             contentDescription = null,
         )
@@ -341,7 +339,7 @@
                             R.string.accessibility_back_arrow_button
                         ),
                         modifier = Modifier.size(24.dp).autoMirrored(),
-                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
+                        tint = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
                     )
                 }
             }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
index 3581228..14bf4f2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
@@ -20,20 +20,20 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentHeight
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 
 @Composable
 fun CredentialListSectionHeader(text: String) {
-    InternalSectionHeader(text, MaterialTheme.colorScheme.onSurfaceVariant)
+    InternalSectionHeader(text, LocalAndroidColorScheme.current.colorOnSurfaceVariant)
 }
 
 @Composable
 fun MoreAboutPasskeySectionHeader(text: String) {
-    InternalSectionHeader(text, MaterialTheme.colorScheme.onSurface)
+    InternalSectionHeader(text, LocalAndroidColorScheme.current.colorOnSurface)
 }
 
 @Composable
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
index 22871bcb..61c03b4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 
 /**
  * The headline for a screen. E.g. "Create a passkey for X", "Choose a saved sign-in for X".
@@ -35,7 +36,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = MaterialTheme.colorScheme.onSurface,
+        color = LocalAndroidColorScheme.current.colorOnSurface,
         textAlign = TextAlign.Center,
         style = MaterialTheme.typography.headlineSmall,
     )
@@ -49,7 +50,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = MaterialTheme.colorScheme.onSurfaceVariant,
+        color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
         style = MaterialTheme.typography.bodyMedium,
     )
 }
@@ -62,7 +63,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = MaterialTheme.colorScheme.onSurfaceVariant,
+        color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
         style = MaterialTheme.typography.bodySmall,
         overflow = TextOverflow.Ellipsis,
         maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
@@ -77,7 +78,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = MaterialTheme.colorScheme.onSurface,
+        color = LocalAndroidColorScheme.current.colorOnSurface,
         style = MaterialTheme.typography.titleLarge,
     )
 }
@@ -90,7 +91,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = MaterialTheme.colorScheme.onSurface,
+        color = LocalAndroidColorScheme.current.colorOnSurface,
         style = MaterialTheme.typography.titleSmall,
         overflow = TextOverflow.Ellipsis,
         maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
@@ -145,7 +146,7 @@
         modifier = modifier.wrapContentSize(),
         text = text,
         textAlign = TextAlign.Center,
-        color = MaterialTheme.colorScheme.onSurfaceVariant,
+        color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
         style = MaterialTheme.typography.labelLarge,
     )
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 648d832..66d7db8 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -30,7 +30,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.material3.Divider
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.NewReleases
 import androidx.compose.material.icons.filled.Add
@@ -67,6 +66,7 @@
 import com.android.credentialmanager.common.ui.PasskeyBenefitRow
 import com.android.credentialmanager.common.ui.HeadlineText
 import com.android.credentialmanager.logging.CreateCredentialEvent
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 import com.android.internal.logging.UiEventLogger.UiEventEnum
 
 @Composable
@@ -559,7 +559,7 @@
             item {
                 Divider(
                     thickness = 1.dp,
-                    color = MaterialTheme.colorScheme.outlineVariant,
+                    color = LocalAndroidColorScheme.current.colorOutlineVariant,
                     modifier = Modifier.padding(vertical = 16.dp)
                 )
             }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 7a86790..c9c62a4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -42,7 +42,7 @@
 }
 
 internal fun isFallbackScreen(state: GetCredentialUiState): Boolean {
-    return false
+    return state.requestDisplayInfo.preferIdentityDocUi
 }
 
 internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): CredentialEntryInfo? {
@@ -172,6 +172,7 @@
 data class RequestDisplayInfo(
     val appName: String,
     val preferImmediatelyAvailableCredentials: Boolean,
+    val preferIdentityDocUi: Boolean,
 )
 
 /**
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
index 8928e18..a33904d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
@@ -42,6 +42,9 @@
 class AndroidColorScheme internal constructor(context: Context) {
     val colorSurfaceBright = getColor(context, R.attr.materialColorSurfaceBright)
     val colorSurfaceContainerHigh = getColor(context, R.attr.materialColorSurfaceContainerHigh)
+    val colorOutlineVariant = getColor(context, R.attr.materialColorOutlineVariant)
+    val colorOnSurface = getColor(context, R.attr.materialColorOnSurface)
+    val colorOnSurfaceVariant = getColor(context, R.attr.materialColorOnSurfaceVariant)
 
     companion object {
         fun getColor(context: Context, attr: Int): Color {
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverLogging.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverLogging.java
new file mode 100644
index 0000000..5326e73
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverLogging.java
@@ -0,0 +1,53 @@
+/*
+ * 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.settingslib.fuelgauge;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utilities related to battery saver logging.
+ */
+public final class BatterySaverLogging {
+    /**
+     * Record the reason while enabling power save mode manually.
+     * See {@link SaverManualEnabledReason} for all available states.
+     */
+    public static final String EXTRA_POWER_SAVE_MODE_MANUAL_ENABLED_REASON =
+            "extra_power_save_mode_manual_enabled_reason";
+
+    /** Broadcast action to record battery saver manual enabled reason. */
+    public static final String ACTION_SAVER_MANUAL_ENABLED_REASON =
+            "com.android.settingslib.fuelgauge.ACTION_SAVER_MANUAL_ENABLED_REASON";
+
+    /** An interface for the battery saver manual enable reason. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SAVER_ENABLED_UNKNOWN, SAVER_ENABLED_CONFIRMATION, SAVER_ENABLED_VOICE,
+            SAVER_ENABLED_SETTINGS, SAVER_ENABLED_QS, SAVER_ENABLED_LOW_WARNING,
+            SAVER_ENABLED_SEVERE_WARNING})
+    public @interface SaverManualEnabledReason {}
+
+    public static final int SAVER_ENABLED_UNKNOWN = 0;
+    public static final int SAVER_ENABLED_CONFIRMATION = 1;
+    public static final int SAVER_ENABLED_VOICE = 2;
+    public static final int SAVER_ENABLED_SETTINGS = 3;
+    public static final int SAVER_ENABLED_QS = 4;
+    public static final int SAVER_ENABLED_LOW_WARNING = 5;
+    public static final int SAVER_ENABLED_SEVERE_WARNING = 6;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 52f3111..a3db6d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -16,6 +16,10 @@
 
 package com.android.settingslib.fuelgauge;
 
+import static com.android.settingslib.fuelgauge.BatterySaverLogging.ACTION_SAVER_MANUAL_ENABLED_REASON;
+import static com.android.settingslib.fuelgauge.BatterySaverLogging.EXTRA_POWER_SAVE_MODE_MANUAL_ENABLED_REASON;
+import static com.android.settingslib.fuelgauge.BatterySaverLogging.SaverManualEnabledReason;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -145,7 +149,8 @@
                         && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
                         && Secure.getInt(cr,
                         Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
-                    showAutoBatterySaverSuggestion(context, confirmationExtras);
+                    sendSystemUiBroadcast(context, ACTION_SHOW_AUTO_SAVER_SUGGESTION,
+                            confirmationExtras);
                 }
             }
 
@@ -175,21 +180,23 @@
             // Already shown.
             return false;
         }
-        context.sendBroadcast(
-                getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, extras));
+        sendSystemUiBroadcast(context, ACTION_SHOW_START_SAVER_CONFIRMATION, extras);
         return true;
     }
 
-    private static void showAutoBatterySaverSuggestion(Context context, Bundle extras) {
-        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, extras));
+    private static void recordBatterySaverEnabledReason(Context context,
+            @SaverManualEnabledReason int reason) {
+        final Bundle enabledReasonExtras = new Bundle(1);
+        enabledReasonExtras.putInt(EXTRA_POWER_SAVE_MODE_MANUAL_ENABLED_REASON, reason);
+        sendSystemUiBroadcast(context, ACTION_SAVER_MANUAL_ENABLED_REASON, enabledReasonExtras);
     }
 
-    private static Intent getSystemUiBroadcast(String action, Bundle extras) {
-        final Intent i = new Intent(action);
-        i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        i.setPackage(SYSUI_PACKAGE);
-        i.putExtras(extras);
-        return i;
+    private static void sendSystemUiBroadcast(Context context, String action, Bundle extras) {
+        final Intent intent = new Intent(action);
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setPackage(SYSUI_PACKAGE);
+        intent.putExtras(extras);
+        context.sendBroadcast(intent);
     }
 
     private static void setBatterySaverConfirmationAcknowledged(Context context) {
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 59cd7a0..a93cd62 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -43,6 +43,8 @@
     <bool name="def_install_non_market_apps">false</bool>
     <!-- 0 == off, 3 == on -->
     <integer name="def_location_mode">3</integer>
+    <!-- 0 == off, 1 == on-->
+    <integer name="def_paired_device_location_mode">1</integer>
     <bool name="assisted_gps_enabled">true</bool>
     <bool name="def_netstats_enabled">true</bool>
     <bool name="def_usb_mass_storage_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index e50f522..41ce58e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -103,5 +103,7 @@
         Settings.Global.Wearable.UPGRADE_DATA_MIGRATION_STATUS,
         Settings.Global.HDR_CONVERSION_MODE,
         Settings.Global.HDR_FORCE_CONVERSION_TYPE,
+        Settings.Global.Wearable.RTL_SWIPE_TO_DISMISS_ENABLED_DEV,
+        Settings.Global.Wearable.REDUCE_MOTION,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index d5386c1..a1c0172 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -285,7 +285,6 @@
                         }));
         VALIDATORS.put(Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.SIDE_BUTTON, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Global.Wearable.BUTTON_SET, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.ANDROID_WEAR_VERSION, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.Wearable.SYSTEM_CAPABILITIES, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.Wearable.SYSTEM_EDITION, ANY_INTEGER_VALIDATOR);
@@ -345,6 +344,7 @@
                             String.valueOf(Global.Wearable.HFP_CLIENT_DISABLED)
                         }));
         VALIDATORS.put(Global.Wearable.COMPANION_OS_VERSION, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.COMPANION_APP_NAME, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.Wearable.ENABLE_ALL_LANGUAGES, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.OEM_SETUP_VERSION, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(
@@ -404,16 +404,6 @@
         VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.BEDTIME_MODE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.BEDTIME_HARD_MODE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(
-                Global.Wearable.EARLY_UPDATES_STATUS,
-                new DiscreteValueValidator(
-                        new String[] {
-                                String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_NOT_STARTED),
-                                String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_STARTED),
-                                String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_SUCCESS),
-                                String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_SKIPPED),
-                                String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_ABORTED),
-                          }));
         VALIDATORS.put(Global.Wearable.DYNAMIC_COLOR_THEME_ENABLED, BOOLEAN_VALIDATOR);
 	VALIDATORS.put(Global.Wearable.SCREENSHOT_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.UPGRADE_DATA_MIGRATION_STATUS,
@@ -423,5 +413,22 @@
                             String.valueOf(Global.Wearable.UPGRADE_DATA_MIGRATION_PENDING),
                             String.valueOf(Global.Wearable.UPGRADE_DATA_MIGRATION_DONE)
                         }));
+        VALIDATORS.put(Global.Wearable.DISABLE_AOD_WHILE_PLUGGED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.NETWORK_LOCATION_OPT_IN, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.PHONE_SWITCHING_STATUS,
+                new InclusiveIntegerRangeValidator(
+                        Global.Wearable.PHONE_SWITCHING_STATUS_NOT_STARTED,
+                        Global.Wearable.PHONE_SWITCHING_STATUS_IN_PROGRESS_MIGRATION_SUCCESS));
+        VALIDATORS.put(Global.Wearable.REDUCE_MOTION, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.RTL_SWIPE_TO_DISMISS_ENABLED_DEV, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.TETHER_CONFIG_STATE,
+                new DiscreteValueValidator(
+                        new String[] {
+                                String.valueOf(Global.Wearable.TETHERED_CONFIG_UNKNOWN),
+                                String.valueOf(Global.Wearable.TETHERED_CONFIG_STANDALONE),
+                                String.valueOf(Global.Wearable.TETHERED_CONFIG_TETHERED)
+                        }));
+        VALIDATORS.put(Global.Wearable.PHONE_SWITCHING_SUPPORTED, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index b0a1927..284b06b 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 = 217;
+            private static final int SETTINGS_VERSION = 218;
 
             private final int mUserId;
 
@@ -5334,74 +5334,73 @@
 
                 if (currentVersion == 203) {
                     // Version 203: initialize entries migrated from wear settings provide.
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.HAS_PAY_TOKENS, false);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, 6);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.HOTWORD_DETECTION_ENABLED,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_hotwordDetectionEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.SMART_REPLIES_ENABLED, true);
                     Setting locationMode =
                             getSecureSettingsLocked(userId).getSettingLocked(Secure.LOCATION_MODE);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
                             !locationMode.isNull()
                                     && !Integer.toString(Secure.LOCATION_MODE_OFF)
                                             .equals(locationMode.getValue()));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY,
                             Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY_UNKNOWN);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.BUG_REPORT,
                             "user".equals(Build.TYPE) // is user build?
                                     ? Global.Wearable.BUG_REPORT_DISABLED
                                     : Global.Wearable.BUG_REPORT_ENABLED);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.SMART_ILLUMINATE_ENABLED,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_smartIlluminateEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.CLOCKWORK_AUTO_TIME,
                             Global.Wearable.SYNC_TIME_FROM_PHONE);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
                             Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.CLOCKWORK_24HR_TIME, false);
-                    initGlobalSettingsDefaultValForWearLocked(Global.Wearable.AUTO_WIFI, true);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(Global.Wearable.AUTO_WIFI, true);
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.WIFI_POWER_SAVE,
                             getContext()
                                     .getResources()
                                     .getInteger(
                                             R.integer
                                                     .def_wearable_offChargerWifiUsageLimitMinutes));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS, 0L);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.SETUP_SKIPPED, Global.Wearable.SETUP_SKIPPED_UNKNOWN);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.LAST_CALL_FORWARD_ACTION,
                             Global.Wearable.CALL_FORWARD_NO_LAST_ACTION);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_muteWhenOffBodyEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.WEAR_OS_VERSION_STRING, "");
-                    initGlobalSettingsDefaultValForWearLocked(Global.Wearable.BUTTON_SET, false);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.SIDE_BUTTON,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_sideButtonPresent));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.ANDROID_WEAR_VERSION,
                             Long.parseLong(
                                     getContext()
@@ -5410,55 +5409,55 @@
                     final int editionGlobal = 1;
                     final int editionLocal = 2;
                     boolean isLe = getContext().getPackageManager().hasSystemFeature("cn.google");
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.SYSTEM_EDITION, isLe ? editionLocal : editionGlobal);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.SYSTEM_CAPABILITIES, getWearSystemCapabilities(isLe));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
                             SystemProperties.getInt("ro.cw_build.platform_mr", 0));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_mobileSignalDetectorAllowed));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.AMBIENT_ENABLED,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_ambientEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.AMBIENT_TILT_TO_WAKE,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_tiltToWakeEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, false);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_touchToWakeEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
                             getContext()
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_tiltToBrightEnabled));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Global.Wearable.DECOMPOSABLE_WATCHFACE, false);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
                             SystemProperties.getBoolean("ro.ambient.force_when_docked", false));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
                             SystemProperties.getBoolean("ro.ambient.low_bit_enabled", false));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
                             SystemProperties.getInt("ro.ambient.plugged_timeout_min", -1));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
                             Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
                             Settings.Global.Wearable.HFP_CLIENT_UNSET);
                     Setting disabledProfileSetting =
@@ -5468,7 +5467,7 @@
                             disabledProfileSetting.isNull()
                                     ? 0
                                     : Long.parseLong(disabledProfileSetting.getValue());
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.COMPANION_OS_VERSION,
                             Settings.Global.Wearable.COMPANION_OS_VERSION_UNDEFINED);
                     final boolean defaultBurnInProtectionEnabled =
@@ -5482,17 +5481,17 @@
                                                     .config_enableBurnInProtection);
                     final boolean forceBurnInProtection =
                             SystemProperties.getBoolean("persist.debug.force_burn_in", false);
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.BURN_IN_PROTECTION_ENABLED,
                             defaultBurnInProtectionEnabled || forceBurnInProtection);
 
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE,
                             getContext()
                                     .getResources()
                                     .getString(
                                             com.android.internal.R.string.config_wearSysUiPackage));
-                    initGlobalSettingsDefaultValForWearLocked(
+                    initGlobalSettingsDefaultValLocked(
                             Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
                             getContext()
                                     .getResources()
@@ -5622,63 +5621,16 @@
                     currentVersion = 210;
                 }
                 if (currentVersion == 210) {
-                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
-                    final Setting currentSetting = secureSettings.getSettingLocked(
-                            Secure.STATUS_BAR_SHOW_VIBRATE_ICON);
-                    if (currentSetting.isNull()) {
-                        final int defaultValueVibrateIconEnabled = getContext().getResources()
-                                .getInteger(R.integer.def_statusBarVibrateIconEnabled);
-                        secureSettings.insertSettingOverrideableByRestoreLocked(
-                                Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
-                                String.valueOf(defaultValueVibrateIconEnabled),
-                                null /* tag */, true /* makeDefault */,
-                                SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
+                    // Unused. Moved to version 217.
                     currentVersion = 211;
                 }
                 if (currentVersion == 211) {
-                    // Version 211: Set default value for
-                    // Secure#LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS
-                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
-                    final Setting lockScreenUnseenSetting = secureSettings
-                            .getSettingLocked(Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS);
-                    if (lockScreenUnseenSetting.isNull()) {
-                        final boolean defSetting = getContext().getResources()
-                                .getBoolean(R.bool.def_lock_screen_show_only_unseen_notifications);
-                        secureSettings.insertSettingOverrideableByRestoreLocked(
-                                Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                                defSetting ? "1" : "0",
-                                null /* tag */,
-                                true /* makeDefault */,
-                                SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
+                    // Unused. Moved to version 217.
                     currentVersion = 212;
                 }
 
                 if (currentVersion == 212) {
-                    final SettingsState globalSettings = getGlobalSettingsLocked();
-                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
-
-                    final Setting bugReportInPowerMenu = globalSettings.getSettingLocked(
-                            Global.BUGREPORT_IN_POWER_MENU);
-
-                    if (!bugReportInPowerMenu.isNull()) {
-                        Slog.i(LOG_TAG, "Setting bugreport_in_power_menu to "
-                                + bugReportInPowerMenu.getValue() + " in Secure settings.");
-                        secureSettings.insertSettingLocked(
-                                Secure.BUGREPORT_IN_POWER_MENU,
-                                bugReportInPowerMenu.getValue(), null /* tag */,
-                                false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
-
-                        // set global bug_report_in_power_menu setting to null since it's deprecated
-                        Slog.i(LOG_TAG, "Setting bugreport_in_power_menu to null"
-                                + " in Global settings since it's deprecated.");
-                        globalSettings.insertSettingLocked(
-                                Global.BUGREPORT_IN_POWER_MENU, null /* value */, null /* tag */,
-                                true /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
+                    // Unused. Moved to version 217.
                     currentVersion = 213;
                 }
 
@@ -5804,6 +5756,89 @@
                     currentVersion = 217;
                 }
 
+                if (currentVersion == 217) {
+                    // Version 217: merge and rebase wear settings init logic.
+
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+
+                    // Following init logic is moved from version 210 to this version in order to
+                    // resolve version conflict with wear branch.
+                    final Setting currentSetting = secureSettings.getSettingLocked(
+                            Secure.STATUS_BAR_SHOW_VIBRATE_ICON);
+                    if (currentSetting.isNull()) {
+                        final int defaultValueVibrateIconEnabled = getContext().getResources()
+                                .getInteger(R.integer.def_statusBarVibrateIconEnabled);
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
+                                String.valueOf(defaultValueVibrateIconEnabled),
+                                null /* tag */, true /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // Set default value for Secure#LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS
+                    // Following init logic is moved from version 211 to this version in order to
+                    // resolve version conflict with wear branch.
+                    final Setting lockScreenUnseenSetting = secureSettings
+                            .getSettingLocked(Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS);
+                    if (lockScreenUnseenSetting.isNull()) {
+                        final boolean defSetting = getContext().getResources()
+                                .getBoolean(R.bool.def_lock_screen_show_only_unseen_notifications);
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+                                defSetting ? "1" : "0",
+                                null /* tag */,
+                                true /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // Following init logic is moved from version 212 to this version in order to
+                    // resolve version conflict with wear branch.
+                    final Setting bugReportInPowerMenu = globalSettings.getSettingLocked(
+                            Global.BUGREPORT_IN_POWER_MENU);
+
+                    if (!bugReportInPowerMenu.isNull()) {
+                        Slog.i(LOG_TAG, "Setting bugreport_in_power_menu to "
+                                + bugReportInPowerMenu.getValue() + " in Secure settings.");
+                        secureSettings.insertSettingLocked(
+                                Secure.BUGREPORT_IN_POWER_MENU,
+                                bugReportInPowerMenu.getValue(), null /* tag */,
+                                false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
+
+                        // set global bug_report_in_power_menu setting to null since it's deprecated
+                        Slog.i(LOG_TAG, "Setting bugreport_in_power_menu to null"
+                                + " in Global settings since it's deprecated.");
+                        globalSettings.insertSettingLocked(
+                                Global.BUGREPORT_IN_POWER_MENU, null /* value */, null /* tag */,
+                                true /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // Following init logic is rebased from wear OS branch.
+                    // Initialize default value of tether configuration to unknown.
+                    initGlobalSettingsDefaultValLocked(
+                            Settings.Global.Wearable.TETHER_CONFIG_STATE,
+                            Global.Wearable.TETHERED_CONFIG_UNKNOWN);
+                    // Init paired device location setting from resources.
+                    initGlobalSettingsDefaultValLocked(
+                            Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+                            getContext()
+                                    .getResources()
+                                    .getInteger(R.integer.def_paired_device_location_mode));
+                    // Init media packages from resources.
+                    final String mediaControlsPackage = getContext().getResources().getString(
+                            com.android.internal.R.string.config_wearMediaControlsPackage);
+                    final String mediaSessionsPackage = getContext().getResources().getString(
+                            com.android.internal.R.string.config_wearMediaSessionsPackage);
+                    initGlobalSettingsDefaultValLocked(
+                            Global.Wearable.WEAR_MEDIA_CONTROLS_PACKAGE,
+                            mediaControlsPackage);
+                    initGlobalSettingsDefaultValLocked(
+                            Global.Wearable.WEAR_MEDIA_SESSIONS_PACKAGE,
+                            mediaSessionsPackage);
+
+                    currentVersion = 218;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
@@ -5821,19 +5856,19 @@
                 return currentVersion;
             }
 
-            private void initGlobalSettingsDefaultValForWearLocked(String key, boolean val) {
-                initGlobalSettingsDefaultValForWearLocked(key, val ? "1" : "0");
+            private void initGlobalSettingsDefaultValLocked(String key, boolean val) {
+                initGlobalSettingsDefaultValLocked(key, val ? "1" : "0");
             }
 
-            private void initGlobalSettingsDefaultValForWearLocked(String key, int val) {
-                initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+            private void initGlobalSettingsDefaultValLocked(String key, int val) {
+                initGlobalSettingsDefaultValLocked(key, String.valueOf(val));
             }
 
-            private void initGlobalSettingsDefaultValForWearLocked(String key, long val) {
-                initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+            private void initGlobalSettingsDefaultValLocked(String key, long val) {
+                initGlobalSettingsDefaultValLocked(key, String.valueOf(val));
             }
 
-            private void initGlobalSettingsDefaultValForWearLocked(String key, String val) {
+            private void initGlobalSettingsDefaultValLocked(String key, String val) {
                 final SettingsState globalSettings = getGlobalSettingsLocked();
                 Setting currentSetting = globalSettings.getSettingLocked(key);
                 if (currentSetting.isNull()) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 19f1a86..a202e16 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -627,7 +627,6 @@
                     Settings.Global.Wearable.STEM_3_DATA,
                     Settings.Global.Wearable.STEM_3_DEFAULT_DATA,
                     Settings.Global.Wearable.WEAR_OS_VERSION_STRING,
-                    Settings.Global.Wearable.BUTTON_SET,
                     Settings.Global.Wearable.SIDE_BUTTON,
                     Settings.Global.Wearable.ANDROID_WEAR_VERSION,
                     Settings.Global.Wearable.SYSTEM_CAPABILITIES,
@@ -643,6 +642,7 @@
                     Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
                     Settings.Global.Wearable.COMPANION_BLE_ROLE,
                     Settings.Global.Wearable.COMPANION_NAME,
+                    Settings.Global.Wearable.COMPANION_APP_NAME,
                     Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
                     Settings.Global.Wearable.COMPANION_OS_VERSION,
                     Settings.Global.Wearable.ENABLE_ALL_LANGUAGES,
@@ -662,13 +662,21 @@
                     Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
                     Settings.Global.Wearable.BEDTIME_MODE,
                     Settings.Global.Wearable.BEDTIME_HARD_MODE,
-                    Settings.Global.Wearable.EARLY_UPDATES_STATUS,
                     Settings.Global.Wearable.RSB_WAKE_ENABLED,
                     Settings.Global.Wearable.LOCK_SCREEN_STATE,
                     Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
                     Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
                     Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_SPEED,
-                    Settings.Global.Wearable.SCREENSHOT_ENABLED);
+                    Settings.Global.Wearable.SCREENSHOT_ENABLED,
+                    Settings.Global.Wearable.DISABLE_AOD_WHILE_PLUGGED,
+                    Settings.Global.Wearable.NETWORK_LOCATION_OPT_IN,
+                    Settings.Global.Wearable.CUSTOM_COLOR_FOREGROUND,
+                    Settings.Global.Wearable.CUSTOM_COLOR_BACKGROUND,
+                    Settings.Global.Wearable.PHONE_SWITCHING_STATUS,
+                    Settings.Global.Wearable.TETHER_CONFIG_STATE,
+                    Settings.Global.Wearable.PHONE_SWITCHING_SUPPORTED,
+                    Settings.Global.Wearable.WEAR_MEDIA_CONTROLS_PACKAGE,
+                    Settings.Global.Wearable.WEAR_MEDIA_SESSIONS_PACKAGE);
 
     private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
              newHashSet(
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/preferences_action_bar.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/preferences_action_bar.xml
new file mode 100644
index 0000000..1d670660
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/preferences_action_bar.xml
@@ -0,0 +1,29 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/action_bar_title"
+        style="@style/TextAppearance.AppCompat.Title"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:maxLines="5"/>
+</LinearLayout>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 02d279f..5ed450a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.accessibilitymenu.activity;
 
+import android.app.ActionBar;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -24,6 +25,7 @@
 import android.os.Bundle;
 import android.provider.Browser;
 import android.provider.Settings;
+import android.widget.TextView;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -46,6 +48,13 @@
                 .beginTransaction()
                 .replace(android.R.id.content, new A11yMenuPreferenceFragment())
                 .commit();
+
+        ActionBar actionBar = getActionBar();
+        actionBar.setDisplayShowCustomEnabled(true);
+        actionBar.setCustomView(R.layout.preferences_action_bar);
+        ((TextView) findViewById(R.id.action_bar_title)).setText(
+                getResources().getString(R.string.accessibility_menu_settings_name)
+        );
     }
 
     /**
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_palette.xml b/packages/SystemUI/res-keyguard/drawable/ic_palette.xml
new file mode 100644
index 0000000..cbea369
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_palette.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,395 112,322Q144,249 199.5,195Q255,141 329.5,110.5Q404,80 489,80Q568,80 639,106.5Q710,133 763.5,180Q817,227 848.5,291.5Q880,356 880,433Q880,541 817,603.5Q754,666 650,666L575,666Q557,666 544,680Q531,694 531,711Q531,738 545.5,757Q560,776 560,801Q560,839 539,859.5Q518,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM247,506Q267,506 282,491Q297,476 297,456Q297,436 282,421Q267,406 247,406Q227,406 212,421Q197,436 197,456Q197,476 212,491Q227,506 247,506ZM373,336Q393,336 408,321Q423,306 423,286Q423,266 408,251Q393,236 373,236Q353,236 338,251Q323,266 323,286Q323,306 338,321Q353,336 373,336ZM587,336Q607,336 622,321Q637,306 637,286Q637,266 622,251Q607,236 587,236Q567,236 552,251Q537,266 537,286Q537,306 552,321Q567,336 587,336ZM718,506Q738,506 753,491Q768,476 768,456Q768,436 753,421Q738,406 718,406Q698,406 683,421Q668,436 668,456Q668,476 683,491Q698,506 718,506ZM480,820Q491,820 495.5,815.5Q500,811 500,801Q500,787 485.5,775Q471,763 471,722Q471,676 501,641Q531,606 577,606L650,606Q726,606 773,561.5Q820,517 820,433Q820,301 720,220.5Q620,140 489,140Q343,140 241.5,238.5Q140,337 140,480Q140,621 239.5,720.5Q339,820 480,820Z"/>
+</vector>
diff --git a/packages/SystemUI/res/anim/keyguard_settings_popup_ease_out_interpolator.xml b/packages/SystemUI/res/anim/keyguard_settings_popup_ease_out_interpolator.xml
new file mode 100644
index 0000000..8c2937c
--- /dev/null
+++ b/packages/SystemUI/res/anim/keyguard_settings_popup_ease_out_interpolator.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.30"
+    android:controlY1="0.00"
+    android:controlX2="0.33"
+    android:controlY2="1.00" />
diff --git a/packages/SystemUI/res/anim/long_press_lock_screen_popup_enter.xml b/packages/SystemUI/res/anim/long_press_lock_screen_popup_enter.xml
new file mode 100644
index 0000000..5fa8822
--- /dev/null
+++ b/packages/SystemUI/res/anim/long_press_lock_screen_popup_enter.xml
@@ -0,0 +1,49 @@
+<?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.
+  -->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+
+    <scale
+        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+        android:duration="200"
+        android:fromXScale="0.5"
+        android:fromYScale="0.5"
+        android:toXScale="1.02"
+        android:toYScale="1.02"
+        android:pivotX="50%"
+        android:pivotY="50%" />
+
+    <scale
+        android:interpolator="@anim/keyguard_settings_popup_ease_out_interpolator"
+        android:startOffset="200"
+        android:duration="200"
+        android:fromXScale="1"
+        android:fromYScale="1"
+        android:toXScale="0.98"
+        android:toYScale="0.98"
+        android:pivotX="50%"
+        android:pivotY="50%" />
+
+    <alpha
+        android:interpolator="@android:anim/linear_interpolator"
+        android:duration="83"
+        android:fromAlpha="0"
+        android:toAlpha="1" />
+
+</set>
diff --git a/packages/SystemUI/res/anim/long_press_lock_screen_popup_exit.xml b/packages/SystemUI/res/anim/long_press_lock_screen_popup_exit.xml
new file mode 100644
index 0000000..a6938de
--- /dev/null
+++ b/packages/SystemUI/res/anim/long_press_lock_screen_popup_exit.xml
@@ -0,0 +1,39 @@
+<?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.
+  -->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+
+    <scale
+        android:interpolator="@android:anim/accelerate_interpolator"
+        android:duration="233"
+        android:fromXScale="1"
+        android:fromYScale="1"
+        android:toXScale="0.5"
+        android:toYScale="0.5"
+        android:pivotX="50%"
+        android:pivotY="50%" />
+
+    <alpha
+        android:interpolator="@android:anim/linear_interpolator"
+        android:delay="150"
+        android:duration="83"
+        android:fromAlpha="1"
+        android:toAlpha="0" />
+
+</set>
diff --git a/packages/SystemUI/res/drawable/ic_qs_no_calling_sms.xml b/packages/SystemUI/res/drawable/ic_shade_no_calling_sms.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_qs_no_calling_sms.xml
rename to packages/SystemUI/res/drawable/ic_shade_no_calling_sms.xml
diff --git a/packages/SystemUI/res/drawable/ic_qs_sim_card.xml b/packages/SystemUI/res/drawable/ic_shade_sim_card.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_qs_sim_card.xml
rename to packages/SystemUI/res/drawable/ic_shade_sim_card.xml
diff --git a/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
index 3807b92..a0ceb81 100644
--- a/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
+++ b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
@@ -17,17 +17,17 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:color="?android:attr/colorControlHighlight">
+    android:color="#4d000000">
     <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <solid android:color="@android:color/white"/>
-            <corners android:radius="28dp" />
+            <corners android:radius="@dimen/keyguard_affordance_fixed_radius" />
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle">
-            <solid android:color="?androidprv:attr/colorSurface" />
-            <corners android:radius="28dp" />
+            <solid android:color="?androidprv:attr/materialColorOnBackground" />
+            <corners android:radius="@dimen/keyguard_affordance_fixed_radius" />
         </shape>
     </item>
 </ripple>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index dffe40b..441f963 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -94,7 +94,7 @@
 
     <include
         android:id="@+id/carrier_group"
-        layout="@layout/qs_carrier_group"
+        layout="@layout/shade_carrier_group"
         app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
         android:minHeight="@dimen/large_screen_shade_header_min_height"
         app:layout_constraintWidth_min="48dp"
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 4048a39..c0f7029 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -20,7 +20,7 @@
     android:id="@+id/keyguard_bottom_area"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
-    android:outlineProvider="none" > <!-- Put it above the status bar header -->
+    android:outlineProvider="none" >
 
     <LinearLayout
         android:id="@+id/keyguard_indication_area"
@@ -59,33 +59,58 @@
 
     </LinearLayout>
 
-    <com.android.systemui.animation.view.LaunchableImageView
-        android:id="@+id/start_button"
-        android:layout_height="@dimen/keyguard_affordance_fixed_height"
-        android:layout_width="@dimen/keyguard_affordance_fixed_width"
-        android:layout_gravity="bottom|start"
-        android:scaleType="fitCenter"
-        android:padding="@dimen/keyguard_affordance_fixed_padding"
-        android:tint="?android:attr/textColorPrimary"
-        android:background="@drawable/keyguard_bottom_affordance_bg"
-        android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
-        android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
-        android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
-        android:visibility="gone" />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_gravity="bottom"
+        >
 
-    <com.android.systemui.animation.view.LaunchableImageView
-        android:id="@+id/end_button"
-        android:layout_height="@dimen/keyguard_affordance_fixed_height"
-        android:layout_width="@dimen/keyguard_affordance_fixed_width"
-        android:layout_gravity="bottom|end"
-        android:scaleType="fitCenter"
-        android:padding="@dimen/keyguard_affordance_fixed_padding"
-        android:tint="?android:attr/textColorPrimary"
-        android:background="@drawable/keyguard_bottom_affordance_bg"
-        android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
-        android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
-        android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
-        android:visibility="gone" />
+        <com.android.systemui.animation.view.LaunchableImageView
+            android:id="@+id/start_button"
+            android:layout_height="@dimen/keyguard_affordance_fixed_height"
+            android:layout_width="@dimen/keyguard_affordance_fixed_width"
+            android:layout_gravity="bottom|start"
+            android:scaleType="fitCenter"
+            android:padding="@dimen/keyguard_affordance_fixed_padding"
+            android:tint="?android:attr/textColorPrimary"
+            android:background="@drawable/keyguard_bottom_affordance_bg"
+            android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
+            android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
+            android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
+            android:visibility="invisible" />
+
+        <FrameLayout
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="24dp"
+            >
+            <include
+                android:id="@+id/keyguard_settings_button"
+                layout="@layout/keyguard_settings_popup_menu"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/keyguard_affordance_fixed_height"
+                android:layout_gravity="center"
+                android:visibility="gone"
+                />
+        </FrameLayout>
+
+        <com.android.systemui.animation.view.LaunchableImageView
+            android:id="@+id/end_button"
+            android:layout_height="@dimen/keyguard_affordance_fixed_height"
+            android:layout_width="@dimen/keyguard_affordance_fixed_width"
+            android:layout_gravity="bottom|end"
+            android:scaleType="fitCenter"
+            android:padding="@dimen/keyguard_affordance_fixed_padding"
+            android:tint="?android:attr/textColorPrimary"
+            android:background="@drawable/keyguard_bottom_affordance_bg"
+            android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
+            android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
+            android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
+            android:visibility="invisible" />
+
+    </LinearLayout>
 
     <FrameLayout
         android:id="@+id/overlay_container"
diff --git a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
index 89d88fe..9d0d783 100644
--- a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
+++ b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
@@ -15,25 +15,23 @@
   ~
   -->
 
-<LinearLayout
+<com.android.systemui.animation.view.LaunchableLinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:minHeight="52dp"
+    android:layout_height="48dp"
     android:orientation="horizontal"
     android:gravity="center_vertical"
     android:background="@drawable/keyguard_settings_popup_menu_background"
-    android:paddingStart="16dp"
-    android:paddingEnd="24dp"
-    android:paddingVertical="16dp">
+    android:padding="12dp">
 
     <ImageView
         android:id="@+id/icon"
-        android:layout_width="20dp"
-        android:layout_height="20dp"
-        android:layout_marginEnd="16dp"
-        android:tint="?android:attr/textColorPrimary"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_marginEnd="8dp"
+        android:tint="?androidprv:attr/materialColorOnSecondaryFixed"
         android:importantForAccessibility="no"
         tools:ignore="UseAppTint" />
 
@@ -42,9 +40,9 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorPrimary"
+        android:textColor="?androidprv:attr/materialColorOnSecondaryFixed"
         android:textSize="14sp"
         android:maxLines="1"
         android:ellipsize="end" />
 
-</LinearLayout>
\ No newline at end of file
+</com.android.systemui.animation.view.LaunchableLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/shade_carrier.xml
similarity index 93%
rename from packages/SystemUI/res/layout/qs_carrier.xml
rename to packages/SystemUI/res/layout/shade_carrier.xml
index a854660..0fed393 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/shade_carrier.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License
   -->
 
-<com.android.systemui.qs.carrier.QSCarrier
+<com.android.systemui.shade.carrier.ShadeCarrier
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/linear_carrier"
     android:layout_width="wrap_content"
@@ -29,7 +29,7 @@
     android:focusable="true" >
 
     <com.android.systemui.util.AutoMarqueeTextView
-        android:id="@+id/qs_carrier_text"
+        android:id="@+id/shade_carrier_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_weight="1"
@@ -53,4 +53,4 @@
         android:layout_marginStart="@dimen/qs_carrier_margin_width"
         android:visibility="gone" />
 
-</com.android.systemui.qs.carrier.QSCarrier>
\ No newline at end of file
+</com.android.systemui.shade.carrier.ShadeCarrier>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/shade_carrier_group.xml
similarity index 87%
rename from packages/SystemUI/res/layout/qs_carrier_group.xml
rename to packages/SystemUI/res/layout/shade_carrier_group.xml
index 6e13ab9..2e8f98c 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/shade_carrier_group.xml
@@ -15,7 +15,7 @@
   -->
 
 <!-- Extends LinearLayout -->
-<com.android.systemui.qs.carrier.QSCarrierGroup
+<com.android.systemui.shade.carrier.ShadeCarrierGroup
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/qs_mobile"
     android:layout_width="0dp"
@@ -39,25 +39,25 @@
         android:visibility="gone"/>
 
     <include
-        layout="@layout/qs_carrier"
+        layout="@layout/shade_carrier"
         android:id="@+id/carrier1"
         android:layout_weight="1"/>
 
     <View
-        android:id="@+id/qs_carrier_divider1"
+        android:id="@+id/shade_carrier_divider1"
         android:layout_width="@dimen/qs_header_carrier_separator_width"
         android:layout_height="match_parent"
         android:visibility="gone"
         android:importantForAccessibility="no"/>
 
     <include
-        layout="@layout/qs_carrier"
+        layout="@layout/shade_carrier"
         android:id="@+id/carrier2"
         android:layout_weight="1"
         android:visibility="gone"/>
 
     <View
-        android:id="@+id/qs_carrier_divider2"
+        android:id="@+id/shade_carrier_divider2"
         android:layout_width="@dimen/qs_header_carrier_separator_width"
         android:layout_height="match_parent"
         android:layout_weight="1"
@@ -65,9 +65,9 @@
         android:importantForAccessibility="no"/>
 
     <include
-        layout="@layout/qs_carrier"
+        layout="@layout/shade_carrier"
         android:id="@+id/carrier3"
         android:layout_weight="1"
         android:visibility="gone"/>
 
-</com.android.systemui.qs.carrier.QSCarrierGroup>
\ No newline at end of file
+</com.android.systemui.shade.carrier.ShadeCarrierGroup>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index a11ffcd..f1fca76 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -120,10 +120,6 @@
         />
     </com.android.systemui.shade.NotificationsQuickSettingsContainer>
 
-    <include
-        layout="@layout/keyguard_bottom_area"
-        android:visibility="gone" />
-
     <ViewStub
         android:id="@+id/keyguard_user_switcher_stub"
         android:layout="@layout/keyguard_user_switcher"
@@ -153,6 +149,10 @@
 
     </com.android.keyguard.LockIconView>
 
+    <include
+        layout="@layout/keyguard_bottom_area"
+        android:visibility="gone" />
+
     <FrameLayout
         android:id="@+id/preview_container"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 714d495..4db42aa 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1774,13 +1774,6 @@
     <dimen name="rear_display_title_top_padding">24dp</dimen>
     <dimen name="rear_display_title_bottom_padding">16dp</dimen>
 
-    <!--
-    Vertical distance between the pointer and the popup menu that shows up on the lock screen when
-    it is long-pressed.
-    -->
-    <dimen name="keyguard_long_press_settings_popup_vertical_offset">96dp</dimen>
-
-
     <!-- Bouncer user switcher margins -->
     <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen>
     <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index befbfab..675ae32 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -36,7 +36,7 @@
          fade_out_complete_frame -->
     <dimen name="percent_displacement_at_fade_out" format="float">0.1066</dimen>
 
-    <integer name="qs_carrier_max_em">7</integer>
+    <integer name="shade_carrier_max_em">7</integer>
 
     <!-- Maximum number of notification icons shown on the Always on Display
         (excluding overflow dot) -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f1777f8..74ae954 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3057,13 +3057,17 @@
     <string name="call_from_work_profile_close">Close</string>
 
     <!--
-    Label for a menu item in a menu that is shown when the user wishes to configure the lock screen.
+    Label for a menu item in a menu that is shown when the user wishes to customize the lock screen.
     Clicking on this menu item takes the user to a screen where they can modify the settings of the
     lock screen.
 
+    It is critical that this text is as short as possible. If needed, translators should feel free
+    to drop "lock screen" from their translation and keep just "Customize" or "Customization", in
+    cases when that verb is not available in their target language.
+
     [CHAR LIMIT=32]
     -->
-    <string name="lock_screen_settings">Lock screen settings</string>
+    <string name="lock_screen_settings">Customize lock screen</string>
 
     <!-- Content description for Wi-Fi not available icon on dream [CHAR LIMIT=NONE]-->
     <string name="wifi_unavailable_dream_overlay_content_description">Wi-Fi not available</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 064cea1..2098aea 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1399,4 +1399,9 @@
     <style name="ShortcutItemBackground">
         <item name="android:background">@color/ksh_key_item_new_background</item>
     </style>
+
+    <style name="LongPressLockScreenAnimation">
+        <item name="android:windowEnterAnimation">@anim/long_press_lock_screen_popup_enter</item>
+        <item name="android:windowExitAnimation">@anim/long_press_lock_screen_popup_exit</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index ac0a3fd..a678edc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -28,7 +28,6 @@
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.phone.AnimatorHandle;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +47,6 @@
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private boolean mAnimateYPos;
     private boolean mKeyguardViewVisibilityAnimating;
-    private AnimatorHandle mKeyguardAnimatorHandle;
     private boolean mLastOccludedState = false;
     private final AnimationProperties mAnimationProperties = new AnimationProperties();
     private final LogBuffer mLogBuffer;
@@ -85,10 +83,6 @@
             boolean keyguardFadingAway,
             boolean goingToFullShade,
             int oldStatusBarState) {
-        if (mKeyguardAnimatorHandle != null) {
-            mKeyguardAnimatorHandle.cancel();
-            mKeyguardAnimatorHandle = null;
-        }
         mView.animate().cancel();
         boolean isOccluded = mKeyguardStateController.isOccluded();
         mKeyguardViewVisibilityAnimating = false;
@@ -122,7 +116,7 @@
                     .setDuration(320)
                     .setInterpolator(Interpolators.ALPHA_IN)
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
-            log("keyguardFadingAway transition w/ Y Animation");
+            log("keyguardFadingAway transition w/ Y Aniamtion");
         } else if (statusBarState == KEYGUARD) {
             if (keyguardFadingAway) {
                 mKeyguardViewVisibilityAnimating = true;
@@ -154,7 +148,7 @@
 
                 // Ask the screen off animation controller to animate the keyguard visibility for us
                 // since it may need to be cancelled due to keyguard lifecycle events.
-                mKeyguardAnimatorHandle = mScreenOffAnimationController.animateInKeyguard(
+                mScreenOffAnimationController.animateInKeyguard(
                         mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
             } else {
                 log("Direct set Visibility to VISIBLE");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ac30311..aabdafb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -563,6 +563,7 @@
                 (TouchProcessorResult.ProcessedTouch) result;
         final NormalizedTouchData data = processedTouch.getTouchData();
 
+        boolean shouldPilfer = false;
         mActivePointerId = processedTouch.getPointerOnSensorId();
         switch (processedTouch.getEvent()) {
             case DOWN:
@@ -581,8 +582,7 @@
                         mStatusBarStateController.isDozing());
 
                 // Pilfer if valid overlap, don't allow following events to reach keyguard
-                mInputManager.pilferPointers(
-                        mOverlay.getOverlayView().getViewRootImpl().getInputToken());
+                shouldPilfer = true;
                 break;
 
             case UP:
@@ -621,6 +621,12 @@
         // Always pilfer pointers that are within sensor area or when alternate bouncer is showing
         if (isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), true)
                 || mAlternateBouncerInteractor.isVisibleState()) {
+            shouldPilfer = true;
+        }
+
+        // Execute the pilfer, never pilfer if a vertical swipe is in progress
+        if (shouldPilfer && mLockscreenShadeTransitionController.getQSDragProgress() == 0f
+                && !mPrimaryBouncerInteractor.isInTransit()) {
             mInputManager.pilferPointers(
                     mOverlay.getOverlayView().getViewRootImpl().getInputToken());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/MotionEventExt.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/MotionEventExt.kt
new file mode 100644
index 0000000..26fc36d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/MotionEventExt.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.common.ui.view
+
+import android.util.MathUtils
+import android.view.MotionEvent
+
+/** Returns the distance from the position of this [MotionEvent] and the given coordinates. */
+fun MotionEvent.distanceFrom(
+    x: Float,
+    y: Float,
+): Float {
+    return MathUtils.dist(this.x, this.y, x, y)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 63a4fd2..7945470 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -85,6 +85,8 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
@@ -116,16 +118,16 @@
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
 
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-
 import dagger.Binds;
 import dagger.BindsOptionalOf;
 import dagger.Module;
 import dagger.Provides;
 
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
+import javax.inject.Named;
+
 /**
  * A dagger module for injecting components of System UI that are required by System UI.
  *
@@ -315,4 +317,11 @@
     @Binds
     abstract LargeScreenShadeInterpolator largeScreensShadeInterpolator(
             LargeScreenShadeInterpolatorImpl impl);
+
+    @SysUISingleton
+    @Provides
+    static VisualInterruptionDecisionProvider provideVisualInterruptionDecisionProvider(
+            NotificationInterruptStateProvider innerProvider) {
+        return new NotificationInterruptStateProviderWrapper(innerProvider);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0913cfc..6bb0f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -132,6 +132,11 @@
     // TODO(b/254512676): Tracking Bug
     @JvmField val LOCKSCREEN_CUSTOM_CLOCKS = releasedFlag(207, "lockscreen_custom_clocks")
 
+    // TODO(b/275694445): Tracking Bug
+    @JvmField
+    val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = unreleasedFlag(208,
+        "lockscreen_without_secure_lock_when_dreaming")
+
     /**
      * Whether the clock on a wide lock screen should use the new "stepping" animation for moving
      * the digits when the clock moves.
@@ -230,6 +235,12 @@
     @JvmField
     val REFACTOR_KEYGUARD_DISMISS_INTENT = unreleasedFlag(231, "refactor_keyguard_dismiss_intent")
 
+    /** Whether to allow long-press on the lock screen to directly open wallpaper picker. */
+    // TODO(b/277220285): Tracking bug.
+    @JvmField
+    val LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP =
+        unreleasedFlag(232, "lock_screen_long_press_directly_opens_wallpaper_picker")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c102c5b5..416b237 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -128,6 +128,8 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -1184,6 +1186,8 @@
     private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
     private Lazy<ScrimController> mScrimControllerLazy;
 
+    private FeatureFlags mFeatureFlags;
+
     /**
      * Injected constructor. See {@link KeyguardModule}.
      */
@@ -1214,7 +1218,8 @@
             Lazy<ShadeController> shadeControllerLazy,
             Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
             Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
-            Lazy<ScrimController> scrimControllerLazy) {
+            Lazy<ScrimController> scrimControllerLazy,
+            FeatureFlags featureFlags) {
         mContext = context;
         mUserTracker = userTracker;
         mFalsingCollector = falsingCollector;
@@ -1269,6 +1274,8 @@
 
         mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS;
         mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS;
+
+        mFeatureFlags = featureFlags;
     }
 
     public void userActivity() {
@@ -1682,14 +1689,17 @@
     }
 
     /**
-     * A dream started.  We should lock after the usual screen-off lock timeout but only
-     * if there is a secure lock pattern.
+     * A dream started. We should lock after the usual screen-off lock timeout regardless if
+     * there is a secure lock pattern or not
      */
     public void onDreamingStarted() {
         mUpdateMonitor.dispatchDreamingStarted();
         synchronized (this) {
+            final boolean alwaysShowKeyguard =
+                mFeatureFlags.isEnabled(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING);
             if (mDeviceInteractive
-                    && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+                && (alwaysShowKeyguard ||
+                mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()))) {
                 doKeyguardLaterLocked();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 6ac51cd..5e71458 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -39,6 +39,7 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -119,7 +120,8 @@
             Lazy<ShadeController> shadeController,
             Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
-            Lazy<ScrimController> scrimControllerLazy) {
+            Lazy<ScrimController> scrimControllerLazy,
+            FeatureFlags featureFlags) {
         return new KeyguardViewMediator(
                 context,
                 userTracker,
@@ -149,7 +151,8 @@
                 shadeController,
                 notificationShadeWindowController,
                 activityLaunchAnimator,
-                scrimControllerLazy);
+                scrimControllerLazy,
+                featureFlags);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index 8ece318..ab4abbf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -19,6 +19,7 @@
 
 import android.content.Context
 import android.os.UserHandle
+import android.util.LayoutDirection
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -113,30 +114,6 @@
                 initialValue = emptyMap(),
             )
 
-    private val _slotPickerRepresentations: List<KeyguardSlotPickerRepresentation> by lazy {
-        fun parseSlot(unparsedSlot: String): Pair<String, Int> {
-            val split = unparsedSlot.split(SLOT_CONFIG_DELIMITER)
-            check(split.size == 2)
-            val slotId = split[0]
-            val slotCapacity = split[1].toInt()
-            return slotId to slotCapacity
-        }
-
-        val unparsedSlots =
-            appContext.resources.getStringArray(R.array.config_keyguardQuickAffordanceSlots)
-
-        val seenSlotIds = mutableSetOf<String>()
-        unparsedSlots.mapNotNull { unparsedSlot ->
-            val (slotId, slotCapacity) = parseSlot(unparsedSlot)
-            check(!seenSlotIds.contains(slotId)) { "Duplicate slot \"$slotId\"!" }
-            seenSlotIds.add(slotId)
-            KeyguardSlotPickerRepresentation(
-                id = slotId,
-                maxSelectedAffordances = slotCapacity,
-            )
-        }
-    }
-
     init {
         legacySettingSyncer.startSyncing()
         dumpManager.registerDumpable("KeyguardQuickAffordances", Dumpster())
@@ -211,7 +188,30 @@
      * each slot and select which affordance(s) is/are installed in each slot on the keyguard.
      */
     fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
-        return _slotPickerRepresentations
+        fun parseSlot(unparsedSlot: String): Pair<String, Int> {
+            val split = unparsedSlot.split(SLOT_CONFIG_DELIMITER)
+            check(split.size == 2)
+            val slotId = split[0]
+            val slotCapacity = split[1].toInt()
+            return slotId to slotCapacity
+        }
+
+        val unparsedSlots =
+            appContext.resources.getStringArray(R.array.config_keyguardQuickAffordanceSlots)
+        if (appContext.resources.configuration.layoutDirection == LayoutDirection.RTL) {
+            unparsedSlots.reverse()
+        }
+
+        val seenSlotIds = mutableSetOf<String>()
+        return unparsedSlots.mapNotNull { unparsedSlot ->
+            val (slotId, slotCapacity) = parseSlot(unparsedSlot)
+            check(!seenSlotIds.contains(slotId)) { "Duplicate slot \"$slotId\"!" }
+            seenSlotIds.add(slotId)
+            KeyguardSlotPickerRepresentation(
+                id = slotId,
+                maxSelectedAffordances = slotCapacity,
+            )
+        }
     }
 
     private inner class Dumpster : Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
index 6525a13..ea6700e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -17,29 +17,29 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.view.accessibility.AccessibilityManager
+import androidx.annotation.VisibleForTesting
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.shared.model.Position
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.domain.model.KeyguardSettingsPopupMenuModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -47,6 +47,7 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /** Business logic for use-cases related to the keyguard long-press feature. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -54,18 +55,16 @@
 class KeyguardLongPressInteractor
 @Inject
 constructor(
-    @Application unsafeContext: Context,
-    @Application scope: CoroutineScope,
+    @Application private val scope: CoroutineScope,
     transitionInteractor: KeyguardTransitionInteractor,
     repository: KeyguardRepository,
-    private val activityStarter: ActivityStarter,
     private val logger: UiEventLogger,
     private val featureFlags: FeatureFlags,
     broadcastDispatcher: BroadcastDispatcher,
+    private val accessibilityManager: AccessibilityManagerWrapper,
 ) {
-    private val appContext = unsafeContext.applicationContext
-
-    private val _isLongPressHandlingEnabled: StateFlow<Boolean> =
+    /** Whether the long-press handling feature should be enabled. */
+    val isLongPressHandlingEnabled: StateFlow<Boolean> =
         if (isFeatureEnabled()) {
                 combine(
                     transitionInteractor.finishedKeyguardState.map {
@@ -84,19 +83,35 @@
                 initialValue = false,
             )
 
-    /** Whether the long-press handling feature should be enabled. */
-    val isLongPressHandlingEnabled: Flow<Boolean> = _isLongPressHandlingEnabled
-
-    private val _menu = MutableStateFlow<KeyguardSettingsPopupMenuModel?>(null)
-    /** Model for a menu that should be shown; `null` when no menu should be shown. */
-    val menu: Flow<KeyguardSettingsPopupMenuModel?> =
-        isLongPressHandlingEnabled.flatMapLatest { isEnabled ->
-            if (isEnabled) {
-                _menu
-            } else {
-                flowOf(null)
+    private val _isMenuVisible = MutableStateFlow(false)
+    /** Model for whether the menu should be shown. */
+    val isMenuVisible: StateFlow<Boolean> =
+        isLongPressHandlingEnabled
+            .flatMapLatest { isEnabled ->
+                if (isEnabled) {
+                    _isMenuVisible.asStateFlow()
+                } else {
+                    // Reset the state so we don't see a menu when long-press handling is enabled
+                    // again in the future.
+                    _isMenuVisible.value = false
+                    flowOf(false)
+                }
             }
-        }
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
+    private val _shouldOpenSettings = MutableStateFlow(false)
+    /**
+     * Whether the long-press accessible "settings" flow should be opened.
+     *
+     * Note that [onSettingsShown] must be invoked to consume this, once the settings are opened.
+     */
+    val shouldOpenSettings = _shouldOpenSettings.asStateFlow()
+
+    private var delayedHideMenuJob: Job? = null
 
     init {
         if (isFeatureEnabled()) {
@@ -110,15 +125,46 @@
     }
 
     /** Notifies that the user has long-pressed on the lock screen. */
-    fun onLongPress(x: Int, y: Int) {
-        if (!_isLongPressHandlingEnabled.value) {
+    fun onLongPress() {
+        if (!isLongPressHandlingEnabled.value) {
             return
         }
 
-        showMenu(
-            x = x,
-            y = y,
-        )
+        if (featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP)) {
+            showSettings()
+        } else {
+            showMenu()
+        }
+    }
+
+    /** Notifies that the user has touched outside of the pop-up. */
+    fun onTouchedOutside() {
+        hideMenu()
+    }
+
+    /** Notifies that the user has started a touch gesture on the menu. */
+    fun onMenuTouchGestureStarted() {
+        cancelAutomaticMenuHiding()
+    }
+
+    /** Notifies that the user has started a touch gesture on the menu. */
+    fun onMenuTouchGestureEnded(isClick: Boolean) {
+        if (isClick) {
+            hideMenu()
+            logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
+            showSettings()
+        } else {
+            scheduleAutomaticMenuHiding()
+        }
+    }
+
+    /** Notifies that the settings UI has been shown, consuming the event to show it. */
+    fun onSettingsShown() {
+        _shouldOpenSettings.value = false
+    }
+
+    private fun showSettings() {
+        _shouldOpenSettings.value = true
     }
 
     private fun isFeatureEnabled(): Boolean {
@@ -126,51 +172,40 @@
             featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI)
     }
 
-    /** Updates application state to ask to show the menu at the given coordinates. */
-    private fun showMenu(
-        x: Int,
-        y: Int,
-    ) {
-        _menu.value =
-            KeyguardSettingsPopupMenuModel(
-                position =
-                    Position(
-                        x = x,
-                        y = y,
-                    ),
-                onClicked = {
-                    hideMenu()
-                    navigateToLockScreenSettings()
-                },
-                onDismissed = { hideMenu() },
-            )
+    /** Updates application state to ask to show the menu. */
+    private fun showMenu() {
+        _isMenuVisible.value = true
+        scheduleAutomaticMenuHiding()
         logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
     }
 
+    private fun scheduleAutomaticMenuHiding() {
+        cancelAutomaticMenuHiding()
+        delayedHideMenuJob =
+            scope.launch {
+                delay(timeOutMs())
+                hideMenu()
+            }
+    }
+
     /** Updates application state to ask to hide the menu. */
     private fun hideMenu() {
-        _menu.value = null
+        cancelAutomaticMenuHiding()
+        _isMenuVisible.value = false
     }
 
-    /** Opens the wallpaper picker screen after the device is unlocked by the user. */
-    private fun navigateToLockScreenSettings() {
-        logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
-        activityStarter.dismissKeyguardThenExecute(
-            /* action= */ {
-                appContext.startActivity(
-                    Intent(Intent.ACTION_SET_WALLPAPER).apply {
-                        flags = Intent.FLAG_ACTIVITY_NEW_TASK
-                        appContext
-                            .getString(R.string.config_wallpaperPickerPackage)
-                            .takeIf { it.isNotEmpty() }
-                            ?.let { packageName -> setPackage(packageName) }
-                    }
-                )
-                true
-            },
-            /* cancel= */ {},
-            /* afterKeyguardGone= */ true,
-        )
+    private fun cancelAutomaticMenuHiding() {
+        delayedHideMenuJob?.cancel()
+        delayedHideMenuJob = null
+    }
+
+    private fun timeOutMs(): Long {
+        return accessibilityManager
+            .getRecommendedTimeoutMillis(
+                DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS.toInt(),
+                AccessibilityManager.FLAG_CONTENT_ICONS or AccessibilityManager.FLAG_CONTENT_TEXT,
+            )
+            .toLong()
     }
 
     enum class LogEvents(
@@ -184,4 +219,8 @@
 
         override fun getId() = _id
     }
+
+    companion object {
+        @VisibleForTesting const val DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS = 5000L
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt
deleted file mode 100644
index 7c61e71..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.keyguard.domain.model
-
-import com.android.systemui.common.shared.model.Position
-
-/** Models a settings popup menu for the lock screen. */
-data class KeyguardSettingsPopupMenuModel(
-    /** Where the menu should be anchored, roughly in screen space. */
-    val position: Position,
-    /** Callback to invoke when the menu gets clicked by the user. */
-    val onClicked: () -> Unit,
-    /** Callback to invoke when the menu gets dismissed by the user. */
-    val onDismissed: () -> Unit,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
new file mode 100644
index 0000000..568db2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.os.VibrationEffect
+import kotlin.time.Duration.Companion.milliseconds
+
+object KeyguardBottomAreaVibrations {
+
+    val ShakeAnimationDuration = 300.milliseconds
+    const val ShakeAnimationCycles = 5f
+
+    private const val SmallVibrationScale = 0.3f
+    private const val BigVibrationScale = 0.6f
+
+    val Shake =
+        VibrationEffect.startComposition()
+            .apply {
+                val vibrationDelayMs =
+                    (ShakeAnimationDuration.inWholeMilliseconds / ShakeAnimationCycles * 2).toInt()
+                val vibrationCount = ShakeAnimationCycles.toInt() * 2
+                repeat(vibrationCount) {
+                    addPrimitive(
+                        VibrationEffect.Composition.PRIMITIVE_TICK,
+                        SmallVibrationScale,
+                        vibrationDelayMs,
+                    )
+                }
+            }
+            .compose()
+
+    val Activated =
+        VibrationEffect.startComposition()
+            .addPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_TICK,
+                BigVibrationScale,
+                0,
+            )
+            .addPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+                0.1f,
+                0,
+            )
+            .compose()
+
+    val Deactivated =
+        VibrationEffect.startComposition()
+            .addPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_TICK,
+                BigVibrationScale,
+                0,
+            )
+            .addPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_QUICK_FALL,
+                0.1f,
+                0,
+            )
+            .compose()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index d63636c..68ac7e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -17,41 +17,42 @@
 package com.android.systemui.keyguard.ui.binder
 
 import android.annotation.SuppressLint
+import android.content.Intent
+import android.graphics.Rect
 import android.graphics.drawable.Animatable2
-import android.os.VibrationEffect
 import android.util.Size
 import android.util.TypedValue
-import android.view.MotionEvent
 import android.view.View
-import android.view.ViewConfiguration
 import android.view.ViewGroup
 import android.view.ViewPropertyAnimator
 import android.widget.ImageView
 import android.widget.TextView
-import androidx.core.animation.CycleInterpolator
-import androidx.core.animation.ObjectAnimator
+import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
 import androidx.core.view.updateLayoutParams
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.settingslib.Utils
 import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.animation.view.LaunchableLinearLayout
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.binder.IconViewBinder
+import com.android.systemui.common.ui.binder.TextViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
-import kotlin.math.pow
-import kotlin.math.sqrt
-import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
@@ -91,15 +92,20 @@
          * icon
          */
         fun shouldConstrainToTopOfLockIcon(): Boolean
+
+        /** Destroys this binding, releases resources, and cancels any coroutines. */
+        fun destroy()
     }
 
     /** Binds the view to the view-model, continuing to update the former based on the latter. */
+    @SuppressLint("ClickableViewAccessibility")
     @JvmStatic
     fun bind(
         view: ViewGroup,
         viewModel: KeyguardBottomAreaViewModel,
         falsingManager: FalsingManager?,
         vibratorHelper: VibratorHelper?,
+        activityStarter: ActivityStarter?,
         messageDisplayer: (Int) -> Unit,
     ): Binding {
         val indicationArea: View = view.requireViewById(R.id.keyguard_indication_area)
@@ -110,137 +116,192 @@
         val indicationText: TextView = view.requireViewById(R.id.keyguard_indication_text)
         val indicationTextBottom: TextView =
             view.requireViewById(R.id.keyguard_indication_text_bottom)
+        val settingsMenu: LaunchableLinearLayout =
+            view.requireViewById(R.id.keyguard_settings_button)
 
         view.clipChildren = false
         view.clipToPadding = false
+        view.setOnTouchListener { _, event ->
+            if (settingsMenu.isVisible) {
+                val hitRect = Rect()
+                settingsMenu.getHitRect(hitRect)
+                if (!hitRect.contains(event.x.toInt(), event.y.toInt())) {
+                    viewModel.onTouchedOutsideLockScreenSettingsMenu()
+                }
+            }
+
+            false
+        }
 
         val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
 
-        view.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    viewModel.startButton.collect { buttonModel ->
-                        updateButton(
+        val disposableHandle =
+            view.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    launch {
+                        viewModel.startButton.collect { buttonModel ->
+                            updateButton(
+                                view = startButton,
+                                viewModel = buttonModel,
+                                falsingManager = falsingManager,
+                                messageDisplayer = messageDisplayer,
+                                vibratorHelper = vibratorHelper,
+                            )
+                        }
+                    }
+
+                    launch {
+                        viewModel.endButton.collect { buttonModel ->
+                            updateButton(
+                                view = endButton,
+                                viewModel = buttonModel,
+                                falsingManager = falsingManager,
+                                messageDisplayer = messageDisplayer,
+                                vibratorHelper = vibratorHelper,
+                            )
+                        }
+                    }
+
+                    launch {
+                        viewModel.isOverlayContainerVisible.collect { isVisible ->
+                            overlayContainer.visibility =
+                                if (isVisible) {
+                                    View.VISIBLE
+                                } else {
+                                    View.INVISIBLE
+                                }
+                        }
+                    }
+
+                    launch {
+                        viewModel.alpha.collect { alpha ->
+                            view.importantForAccessibility =
+                                if (alpha == 0f) {
+                                    View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                                } else {
+                                    View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+                                }
+
+                            ambientIndicationArea?.alpha = alpha
+                            indicationArea.alpha = alpha
+                        }
+                    }
+
+                    launch {
+                        updateButtonAlpha(
                             view = startButton,
-                            viewModel = buttonModel,
-                            falsingManager = falsingManager,
-                            messageDisplayer = messageDisplayer,
-                            vibratorHelper = vibratorHelper,
+                            viewModel = viewModel.startButton,
+                            alphaFlow = viewModel.alpha,
                         )
                     }
-                }
 
-                launch {
-                    viewModel.endButton.collect { buttonModel ->
-                        updateButton(
+                    launch {
+                        updateButtonAlpha(
                             view = endButton,
-                            viewModel = buttonModel,
-                            falsingManager = falsingManager,
-                            messageDisplayer = messageDisplayer,
-                            vibratorHelper = vibratorHelper,
+                            viewModel = viewModel.endButton,
+                            alphaFlow = viewModel.alpha,
                         )
                     }
-                }
 
-                launch {
-                    viewModel.isOverlayContainerVisible.collect { isVisible ->
-                        overlayContainer.visibility =
+                    launch {
+                        viewModel.indicationAreaTranslationX.collect { translationX ->
+                            indicationArea.translationX = translationX
+                            ambientIndicationArea?.translationX = translationX
+                        }
+                    }
+
+                    launch {
+                        combine(
+                                viewModel.isIndicationAreaPadded,
+                                configurationBasedDimensions.map { it.indicationAreaPaddingPx },
+                            ) { isPadded, paddingIfPaddedPx ->
+                                if (isPadded) {
+                                    paddingIfPaddedPx
+                                } else {
+                                    0
+                                }
+                            }
+                            .collect { paddingPx ->
+                                indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
+                            }
+                    }
+
+                    launch {
+                        configurationBasedDimensions
+                            .map { it.defaultBurnInPreventionYOffsetPx }
+                            .flatMapLatest { defaultBurnInOffsetY ->
+                                viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
+                            }
+                            .collect { translationY ->
+                                indicationArea.translationY = translationY
+                                ambientIndicationArea?.translationY = translationY
+                            }
+                    }
+
+                    launch {
+                        configurationBasedDimensions.collect { dimensions ->
+                            indicationText.setTextSize(
+                                TypedValue.COMPLEX_UNIT_PX,
+                                dimensions.indicationTextSizePx.toFloat(),
+                            )
+                            indicationTextBottom.setTextSize(
+                                TypedValue.COMPLEX_UNIT_PX,
+                                dimensions.indicationTextSizePx.toFloat(),
+                            )
+
+                            startButton.updateLayoutParams<ViewGroup.LayoutParams> {
+                                width = dimensions.buttonSizePx.width
+                                height = dimensions.buttonSizePx.height
+                            }
+                            endButton.updateLayoutParams<ViewGroup.LayoutParams> {
+                                width = dimensions.buttonSizePx.width
+                                height = dimensions.buttonSizePx.height
+                            }
+                        }
+                    }
+
+                    launch {
+                        viewModel.settingsMenuViewModel.isVisible.distinctUntilChanged().collect {
+                            isVisible ->
+                            settingsMenu.animateVisibility(visible = isVisible)
                             if (isVisible) {
-                                View.VISIBLE
-                            } else {
-                                View.INVISIBLE
-                            }
-                    }
-                }
-
-                launch {
-                    viewModel.alpha.collect { alpha ->
-                        view.importantForAccessibility =
-                            if (alpha == 0f) {
-                                View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                            } else {
-                                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                            }
-
-                        ambientIndicationArea?.alpha = alpha
-                        indicationArea.alpha = alpha
-                    }
-                }
-
-                launch {
-                    updateButtonAlpha(
-                        view = startButton,
-                        viewModel = viewModel.startButton,
-                        alphaFlow = viewModel.alpha,
-                    )
-                }
-
-                launch {
-                    updateButtonAlpha(
-                        view = endButton,
-                        viewModel = viewModel.endButton,
-                        alphaFlow = viewModel.alpha,
-                    )
-                }
-
-                launch {
-                    viewModel.indicationAreaTranslationX.collect { translationX ->
-                        indicationArea.translationX = translationX
-                        ambientIndicationArea?.translationX = translationX
-                    }
-                }
-
-                launch {
-                    combine(
-                            viewModel.isIndicationAreaPadded,
-                            configurationBasedDimensions.map { it.indicationAreaPaddingPx },
-                        ) { isPadded, paddingIfPaddedPx ->
-                            if (isPadded) {
-                                paddingIfPaddedPx
-                            } else {
-                                0
+                                vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
+                                settingsMenu.setOnTouchListener(
+                                    KeyguardSettingsButtonOnTouchListener(
+                                        view = settingsMenu,
+                                        viewModel = viewModel.settingsMenuViewModel,
+                                    )
+                                )
+                                IconViewBinder.bind(
+                                    icon = viewModel.settingsMenuViewModel.icon,
+                                    view = settingsMenu.requireViewById(R.id.icon),
+                                )
+                                TextViewBinder.bind(
+                                    view = settingsMenu.requireViewById(R.id.text),
+                                    viewModel = viewModel.settingsMenuViewModel.text,
+                                )
                             }
                         }
-                        .collect { paddingPx ->
-                            indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
-                        }
-                }
+                    }
 
-                launch {
-                    configurationBasedDimensions
-                        .map { it.defaultBurnInPreventionYOffsetPx }
-                        .flatMapLatest { defaultBurnInOffsetY ->
-                            viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
-                        }
-                        .collect { translationY ->
-                            indicationArea.translationY = translationY
-                            ambientIndicationArea?.translationY = translationY
-                        }
-                }
-
-                launch {
-                    configurationBasedDimensions.collect { dimensions ->
-                        indicationText.setTextSize(
-                            TypedValue.COMPLEX_UNIT_PX,
-                            dimensions.indicationTextSizePx.toFloat(),
-                        )
-                        indicationTextBottom.setTextSize(
-                            TypedValue.COMPLEX_UNIT_PX,
-                            dimensions.indicationTextSizePx.toFloat(),
-                        )
-
-                        startButton.updateLayoutParams<ViewGroup.LayoutParams> {
-                            width = dimensions.buttonSizePx.width
-                            height = dimensions.buttonSizePx.height
-                        }
-                        endButton.updateLayoutParams<ViewGroup.LayoutParams> {
-                            width = dimensions.buttonSizePx.width
-                            height = dimensions.buttonSizePx.height
+                    // activityStarter will only be null when rendering the preview that
+                    // shows up in the Wallpaper Picker app. If we do that, then the
+                    // settings menu should never be visible.
+                    if (activityStarter != null) {
+                        launch {
+                            viewModel.settingsMenuViewModel.shouldOpenSettings
+                                .filter { it }
+                                .collect {
+                                    navigateToLockScreenSettings(
+                                        activityStarter = activityStarter,
+                                        view = settingsMenu,
+                                    )
+                                    viewModel.settingsMenuViewModel.onSettingsShown()
+                                }
                         }
                     }
                 }
             }
-        }
 
         return object : Binding {
             override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> {
@@ -253,6 +314,10 @@
 
             override fun shouldConstrainToTopOfLockIcon(): Boolean =
                 viewModel.shouldConstrainToTopOfLockIcon()
+
+            override fun destroy() {
+                disposableHandle.dispose()
+            }
         }
     }
 
@@ -265,7 +330,7 @@
         vibratorHelper: VibratorHelper?,
     ) {
         if (!viewModel.isVisible) {
-            view.isVisible = false
+            view.isInvisible = true
             return
         }
 
@@ -342,7 +407,7 @@
         if (viewModel.isClickable) {
             if (viewModel.useLongPress) {
                 view.setOnTouchListener(
-                    OnTouchListener(
+                    KeyguardQuickAffordanceOnTouchListener(
                         view,
                         viewModel,
                         messageDisplayer,
@@ -372,187 +437,21 @@
             .collect { view.alpha = it }
     }
 
-    private class OnTouchListener(
-        private val view: View,
-        private val viewModel: KeyguardQuickAffordanceViewModel,
-        private val messageDisplayer: (Int) -> Unit,
-        private val vibratorHelper: VibratorHelper?,
-        private val falsingManager: FalsingManager?,
-    ) : View.OnTouchListener {
-
-        private val longPressDurationMs = ViewConfiguration.getLongPressTimeout().toLong()
-        private var longPressAnimator: ViewPropertyAnimator? = null
-
-        @SuppressLint("ClickableViewAccessibility")
-        override fun onTouch(v: View?, event: MotionEvent?): Boolean {
-            return when (event?.actionMasked) {
-                MotionEvent.ACTION_DOWN ->
-                    if (viewModel.configKey != null) {
-                        if (isUsingAccurateTool(event)) {
-                            // For accurate tool types (stylus, mouse, etc.), we don't require a
-                            // long-press.
-                        } else {
-                            // When not using a stylus, we require a long-press to activate the
-                            // quick affordance, mostly to do "falsing" (e.g. protect from false
-                            // clicks in the pocket/bag).
-                            longPressAnimator =
-                                view
-                                    .animate()
-                                    .scaleX(PRESSED_SCALE)
-                                    .scaleY(PRESSED_SCALE)
-                                    .setDuration(longPressDurationMs)
-                                    .withEndAction {
-                                        if (
-                                            falsingManager
-                                                ?.isFalseLongTap(
-                                                    FalsingManager.MODERATE_PENALTY
-                                                ) == false
-                                        ) {
-                                            dispatchClick(viewModel.configKey)
-                                        }
-                                        cancel()
-                                    }
-                        }
-                        true
-                    } else {
-                        false
-                    }
-                MotionEvent.ACTION_MOVE -> {
-                    if (!isUsingAccurateTool(event)) {
-                        // Moving too far while performing a long-press gesture cancels that
-                        // gesture.
-                        val distanceMoved = distanceMoved(event)
-                        if (distanceMoved > ViewConfiguration.getTouchSlop()) {
-                            cancel()
-                        }
-                    }
-                    true
-                }
-                MotionEvent.ACTION_UP -> {
-                    if (isUsingAccurateTool(event)) {
-                        // When using an accurate tool type (stylus, mouse, etc.), we don't require
-                        // a long-press gesture to activate the quick affordance. Therefore, lifting
-                        // the pointer performs a click.
-                        if (
-                            viewModel.configKey != null &&
-                                distanceMoved(event) <= ViewConfiguration.getTouchSlop() &&
-                                falsingManager?.isFalseTap(FalsingManager.NO_PENALTY) == false
-                        ) {
-                            dispatchClick(viewModel.configKey)
-                        }
-                    } else {
-                        // When not using a stylus, lifting the finger/pointer will actually cancel
-                        // the long-press gesture. Calling cancel after the quick affordance was
-                        // already long-press activated is a no-op, so it's safe to call from here.
-                        cancel(
-                            onAnimationEnd =
-                                if (event.eventTime - event.downTime < longPressDurationMs) {
-                                    Runnable {
-                                        messageDisplayer.invoke(
-                                            R.string.keyguard_affordance_press_too_short
-                                        )
-                                        val amplitude =
-                                            view.context.resources
-                                                .getDimensionPixelSize(
-                                                    R.dimen.keyguard_affordance_shake_amplitude
-                                                )
-                                                .toFloat()
-                                        val shakeAnimator =
-                                            ObjectAnimator.ofFloat(
-                                                view,
-                                                "translationX",
-                                                -amplitude / 2,
-                                                amplitude / 2,
-                                            )
-                                        shakeAnimator.duration =
-                                            ShakeAnimationDuration.inWholeMilliseconds
-                                        shakeAnimator.interpolator =
-                                            CycleInterpolator(ShakeAnimationCycles)
-                                        shakeAnimator.start()
-
-                                        vibratorHelper?.vibrate(Vibrations.Shake)
-                                    }
-                                } else {
-                                    null
-                                }
-                        )
-                    }
-                    true
-                }
-                MotionEvent.ACTION_CANCEL -> {
-                    cancel()
-                    true
-                }
-                else -> false
-            }
-        }
-
-        private fun dispatchClick(
-            configKey: String,
-        ) {
-            view.setOnClickListener {
-                vibratorHelper?.vibrate(
-                    if (viewModel.isActivated) {
-                        Vibrations.Activated
-                    } else {
-                        Vibrations.Deactivated
-                    }
-                )
-                viewModel.onClicked(
-                    KeyguardQuickAffordanceViewModel.OnClickedParameters(
-                        configKey = configKey,
-                        expandable = Expandable.fromView(view),
-                        slotId = viewModel.slotId,
-                    )
-                )
-            }
-            view.performClick()
-            view.setOnClickListener(null)
-        }
-
-        private fun cancel(onAnimationEnd: Runnable? = null) {
-            longPressAnimator?.cancel()
-            longPressAnimator = null
-            view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
-        }
-
-        companion object {
-            private const val PRESSED_SCALE = 1.5f
-
-            /**
-             * Returns `true` if the tool type at the given pointer index is an accurate tool (like
-             * stylus or mouse), which means we can trust it to not be a false click; `false`
-             * otherwise.
-             */
-            private fun isUsingAccurateTool(
-                event: MotionEvent,
-                pointerIndex: Int = 0,
-            ): Boolean {
-                return when (event.getToolType(pointerIndex)) {
-                    MotionEvent.TOOL_TYPE_STYLUS -> true
-                    MotionEvent.TOOL_TYPE_MOUSE -> true
-                    else -> false
+    private fun View.animateVisibility(visible: Boolean) {
+        animate()
+            .withStartAction {
+                if (visible) {
+                    alpha = 0f
+                    isVisible = true
                 }
             }
-
-            /**
-             * Returns the amount of distance the pointer moved since the historical record at the
-             * [since] index.
-             */
-            private fun distanceMoved(
-                event: MotionEvent,
-                since: Int = 0,
-            ): Float {
-                return if (event.historySize > 0) {
-                    sqrt(
-                        (event.y - event.getHistoricalY(since)).pow(2) +
-                            (event.x - event.getHistoricalX(since)).pow(2)
-                    )
-                } else {
-                    0f
+            .alpha(if (visible) 1f else 0f)
+            .withEndAction {
+                if (!visible) {
+                    isVisible = false
                 }
             }
-        }
+            .start()
     }
 
     private class OnClickListener(
@@ -594,64 +493,28 @@
         )
     }
 
+    /** Opens the wallpaper picker screen after the device is unlocked by the user. */
+    private fun navigateToLockScreenSettings(
+        activityStarter: ActivityStarter,
+        view: View,
+    ) {
+        activityStarter.startActivity(
+            Intent(Intent.ACTION_SET_WALLPAPER).apply {
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK
+                view.context
+                    .getString(R.string.config_wallpaperPickerPackage)
+                    .takeIf { it.isNotEmpty() }
+                    ?.let { packageName -> setPackage(packageName) }
+            },
+            /* dismissShade= */ true,
+            ActivityLaunchAnimator.Controller.fromView(view),
+        )
+    }
+
     private data class ConfigurationBasedDimensions(
         val defaultBurnInPreventionYOffsetPx: Int,
         val indicationAreaPaddingPx: Int,
         val indicationTextSizePx: Int,
         val buttonSizePx: Size,
     )
-
-    private val ShakeAnimationDuration = 300.milliseconds
-    private val ShakeAnimationCycles = 5f
-
-    object Vibrations {
-
-        private const val SmallVibrationScale = 0.3f
-        private const val BigVibrationScale = 0.6f
-
-        val Shake =
-            VibrationEffect.startComposition()
-                .apply {
-                    val vibrationDelayMs =
-                        (ShakeAnimationDuration.inWholeMilliseconds / (ShakeAnimationCycles * 2))
-                            .toInt()
-                    val vibrationCount = ShakeAnimationCycles.toInt() * 2
-                    repeat(vibrationCount) {
-                        addPrimitive(
-                            VibrationEffect.Composition.PRIMITIVE_TICK,
-                            SmallVibrationScale,
-                            vibrationDelayMs,
-                        )
-                    }
-                }
-                .compose()
-
-        val Activated =
-            VibrationEffect.startComposition()
-                .addPrimitive(
-                    VibrationEffect.Composition.PRIMITIVE_TICK,
-                    BigVibrationScale,
-                    0,
-                )
-                .addPrimitive(
-                    VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
-                    0.1f,
-                    0,
-                )
-                .compose()
-
-        val Deactivated =
-            VibrationEffect.startComposition()
-                .addPrimitive(
-                    VibrationEffect.Composition.PRIMITIVE_TICK,
-                    BigVibrationScale,
-                    0,
-                )
-                .addPrimitive(
-                    VibrationEffect.Composition.PRIMITIVE_QUICK_FALL,
-                    0.1f,
-                    0,
-                )
-                .compose()
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt
deleted file mode 100644
index d85682b..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.keyguard.ui.binder
-
-import android.annotation.SuppressLint
-import android.view.Gravity
-import android.view.LayoutInflater
-import android.view.View
-import android.view.WindowManager
-import android.widget.PopupWindow
-import com.android.systemui.R
-import com.android.systemui.common.ui.binder.IconViewBinder
-import com.android.systemui.common.ui.binder.TextViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsPopupMenuViewModel
-
-object KeyguardLongPressPopupViewBinder {
-    @SuppressLint("InflateParams") // We don't care that the parent is null.
-    fun createAndShow(
-        container: View,
-        viewModel: KeyguardSettingsPopupMenuViewModel,
-        onDismissed: () -> Unit,
-    ): () -> Unit {
-        val contentView: View =
-            LayoutInflater.from(container.context)
-                .inflate(
-                    R.layout.keyguard_settings_popup_menu,
-                    null,
-                )
-
-        contentView.setOnClickListener { viewModel.onClicked() }
-        IconViewBinder.bind(
-            icon = viewModel.icon,
-            view = contentView.requireViewById(R.id.icon),
-        )
-        TextViewBinder.bind(
-            view = contentView.requireViewById(R.id.text),
-            viewModel = viewModel.text,
-        )
-
-        val popupWindow =
-            PopupWindow(container.context).apply {
-                windowLayoutType = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
-                setBackgroundDrawable(null)
-                animationStyle = com.android.internal.R.style.Animation_Dialog
-                isOutsideTouchable = true
-                isFocusable = true
-                setContentView(contentView)
-                setOnDismissListener { onDismissed() }
-                contentView.measure(
-                    View.MeasureSpec.makeMeasureSpec(
-                        0,
-                        View.MeasureSpec.UNSPECIFIED,
-                    ),
-                    View.MeasureSpec.makeMeasureSpec(
-                        0,
-                        View.MeasureSpec.UNSPECIFIED,
-                    ),
-                )
-                showAtLocation(
-                    container,
-                    Gravity.NO_GRAVITY,
-                    viewModel.position.x - contentView.measuredWidth / 2,
-                    viewModel.position.y -
-                        contentView.measuredHeight -
-                        container.context.resources.getDimensionPixelSize(
-                            R.dimen.keyguard_long_press_settings_popup_vertical_offset
-                        ),
-                )
-            }
-
-        return { popupWindow.dismiss() }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
index 8671753..9cc503c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -50,10 +50,7 @@
                         return
                     }
 
-                    viewModel.onLongPress(
-                        x = x,
-                        y = y,
-                    )
+                    viewModel.onLongPress()
                 }
 
                 override fun onSingleTapDetected(view: View) {
@@ -72,23 +69,6 @@
                         view.setLongPressHandlingEnabled(isEnabled)
                     }
                 }
-
-                launch {
-                    var dismissMenu: (() -> Unit)? = null
-
-                    viewModel.menu.collect { menuOrNull ->
-                        if (menuOrNull != null) {
-                            dismissMenu =
-                                KeyguardLongPressPopupViewBinder.createAndShow(
-                                    container = view,
-                                    viewModel = menuOrNull,
-                                    onDismissed = menuOrNull.onDismissed,
-                                )
-                        } else {
-                            dismissMenu?.invoke()
-                        }
-                    }
-                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
new file mode 100644
index 0000000..779095c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.keyguard.ui.binder
+
+import android.annotation.SuppressLint
+import android.graphics.PointF
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import android.view.ViewPropertyAnimator
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.ui.view.distanceFrom
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.VibratorHelper
+
+class KeyguardQuickAffordanceOnTouchListener(
+    private val view: View,
+    private val viewModel: KeyguardQuickAffordanceViewModel,
+    private val messageDisplayer: (Int) -> Unit,
+    private val vibratorHelper: VibratorHelper?,
+    private val falsingManager: FalsingManager?,
+) : View.OnTouchListener {
+
+    private val longPressDurationMs = ViewConfiguration.getLongPressTimeout().toLong()
+    private var longPressAnimator: ViewPropertyAnimator? = null
+    private val down: PointF by lazy { PointF() }
+
+    @SuppressLint("ClickableViewAccessibility")
+    override fun onTouch(v: View, event: MotionEvent): Boolean {
+        return when (event.actionMasked) {
+            MotionEvent.ACTION_DOWN ->
+                if (viewModel.configKey != null) {
+                    down.set(event.x, event.y)
+                    if (isUsingAccurateTool(event)) {
+                        // For accurate tool types (stylus, mouse, etc.), we don't require a
+                        // long-press.
+                    } else {
+                        // When not using a stylus, we require a long-press to activate the
+                        // quick affordance, mostly to do "falsing" (e.g. protect from false
+                        // clicks in the pocket/bag).
+                        longPressAnimator =
+                            view
+                                .animate()
+                                .scaleX(PRESSED_SCALE)
+                                .scaleY(PRESSED_SCALE)
+                                .setDuration(longPressDurationMs)
+                                .withEndAction {
+                                    if (
+                                        falsingManager?.isFalseLongTap(
+                                            FalsingManager.MODERATE_PENALTY
+                                        ) == false
+                                    ) {
+                                        dispatchClick(viewModel.configKey)
+                                    }
+                                    cancel()
+                                }
+                    }
+                    true
+                } else {
+                    false
+                }
+            MotionEvent.ACTION_MOVE -> {
+                if (!isUsingAccurateTool(event)) {
+                    // Moving too far while performing a long-press gesture cancels that
+                    // gesture.
+                    if (event.distanceFrom(down.x, down.y) > ViewConfiguration.getTouchSlop()) {
+                        cancel()
+                    }
+                }
+                true
+            }
+            MotionEvent.ACTION_UP -> {
+                if (isUsingAccurateTool(event)) {
+                    // When using an accurate tool type (stylus, mouse, etc.), we don't require
+                    // a long-press gesture to activate the quick affordance. Therefore, lifting
+                    // the pointer performs a click.
+                    if (
+                        viewModel.configKey != null &&
+                            event.distanceFrom(down.x, down.y) <=
+                                ViewConfiguration.getTouchSlop() &&
+                            falsingManager?.isFalseTap(FalsingManager.NO_PENALTY) == false
+                    ) {
+                        dispatchClick(viewModel.configKey)
+                    }
+                } else {
+                    // When not using a stylus, lifting the finger/pointer will actually cancel
+                    // the long-press gesture. Calling cancel after the quick affordance was
+                    // already long-press activated is a no-op, so it's safe to call from here.
+                    cancel(
+                        onAnimationEnd =
+                            if (event.eventTime - event.downTime < longPressDurationMs) {
+                                Runnable {
+                                    messageDisplayer.invoke(
+                                        R.string.keyguard_affordance_press_too_short
+                                    )
+                                    val amplitude =
+                                        view.context.resources
+                                            .getDimensionPixelSize(
+                                                R.dimen.keyguard_affordance_shake_amplitude
+                                            )
+                                            .toFloat()
+                                    val shakeAnimator =
+                                        ObjectAnimator.ofFloat(
+                                            view,
+                                            "translationX",
+                                            -amplitude / 2,
+                                            amplitude / 2,
+                                        )
+                                    shakeAnimator.duration =
+                                        KeyguardBottomAreaVibrations.ShakeAnimationDuration
+                                            .inWholeMilliseconds
+                                    shakeAnimator.interpolator =
+                                        CycleInterpolator(
+                                            KeyguardBottomAreaVibrations.ShakeAnimationCycles
+                                        )
+                                    shakeAnimator.start()
+
+                                    vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+                                }
+                            } else {
+                                null
+                            }
+                    )
+                }
+                true
+            }
+            MotionEvent.ACTION_CANCEL -> {
+                cancel()
+                true
+            }
+            else -> false
+        }
+    }
+
+    private fun dispatchClick(
+        configKey: String,
+    ) {
+        view.setOnClickListener {
+            vibratorHelper?.vibrate(
+                if (viewModel.isActivated) {
+                    KeyguardBottomAreaVibrations.Activated
+                } else {
+                    KeyguardBottomAreaVibrations.Deactivated
+                }
+            )
+            viewModel.onClicked(
+                KeyguardQuickAffordanceViewModel.OnClickedParameters(
+                    configKey = configKey,
+                    expandable = Expandable.fromView(view),
+                    slotId = viewModel.slotId,
+                )
+            )
+        }
+        view.performClick()
+        view.setOnClickListener(null)
+    }
+
+    private fun cancel(onAnimationEnd: Runnable? = null) {
+        longPressAnimator?.cancel()
+        longPressAnimator = null
+        view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
+    }
+
+    companion object {
+        private const val PRESSED_SCALE = 1.5f
+
+        /**
+         * Returns `true` if the tool type at the given pointer index is an accurate tool (like
+         * stylus or mouse), which means we can trust it to not be a false click; `false` otherwise.
+         */
+        private fun isUsingAccurateTool(
+            event: MotionEvent,
+            pointerIndex: Int = 0,
+        ): Boolean {
+            return when (event.getToolType(pointerIndex)) {
+                MotionEvent.TOOL_TYPE_STYLUS -> true
+                MotionEvent.TOOL_TYPE_MOUSE -> true
+                else -> false
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
new file mode 100644
index 0000000..ad3fb63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.graphics.PointF
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import com.android.systemui.animation.view.LaunchableLinearLayout
+import com.android.systemui.common.ui.view.distanceFrom
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+
+class KeyguardSettingsButtonOnTouchListener(
+    private val view: LaunchableLinearLayout,
+    private val viewModel: KeyguardSettingsMenuViewModel,
+) : View.OnTouchListener {
+
+    private val downPosition = PointF()
+
+    override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
+        when (motionEvent.actionMasked) {
+            MotionEvent.ACTION_DOWN -> {
+                view.isPressed = true
+                downPosition.set(motionEvent.x, motionEvent.y)
+                viewModel.onTouchGestureStarted()
+            }
+            MotionEvent.ACTION_UP -> {
+                view.isPressed = false
+                val distanceMoved = motionEvent.distanceFrom(downPosition.x, downPosition.y)
+                val isClick = distanceMoved < ViewConfiguration.getTouchSlop()
+                viewModel.onTouchGestureEnded(isClick)
+                if (isClick) {
+                    view.performClick()
+                }
+            }
+            MotionEvent.ACTION_CANCEL -> {
+                view.isPressed = false
+                viewModel.onTouchGestureEnded(/* isClick= */ false)
+            }
+        }
+
+        return true
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index a8e3464..2d83be95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -44,6 +44,8 @@
     private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
     private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
     private val burnInHelperWrapper: BurnInHelperWrapper,
+    private val longPressViewModel: KeyguardLongPressViewModel,
+    val settingsMenuViewModel: KeyguardSettingsMenuViewModel,
 ) {
     data class PreviewMode(
         val isInPreviewMode: Boolean = false,
@@ -161,6 +163,14 @@
         selectedPreviewSlotId.value = slotId
     }
 
+    /**
+     * Notifies that some input gesture has started somewhere in the bottom area that's outside of
+     * the lock screen settings menu item pop-up.
+     */
+    fun onTouchedOutsideLockScreenSettingsMenu() {
+        longPressViewModel.onTouchedOutside()
+    }
+
     private fun button(
         position: KeyguardQuickAffordancePosition
     ): Flow<KeyguardQuickAffordanceViewModel> {
@@ -225,9 +235,10 @@
                     isDimmed = isDimmed,
                     slotId = slotId,
                 )
-            is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel(
-                slotId = slotId,
-            )
+            is KeyguardQuickAffordanceModel.Hidden ->
+                KeyguardQuickAffordanceViewModel(
+                    slotId = slotId,
+                )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
index d896390..c73931a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
@@ -17,15 +17,13 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.R
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
 
 /** Models UI state to support the lock screen long-press feature. */
+@SysUISingleton
 class KeyguardLongPressViewModel
 @Inject
 constructor(
@@ -35,35 +33,16 @@
     /** Whether the long-press handling feature should be enabled. */
     val isLongPressHandlingEnabled: Flow<Boolean> = interactor.isLongPressHandlingEnabled
 
-    /** View-model for a menu that should be shown; `null` when no menu should be shown. */
-    val menu: Flow<KeyguardSettingsPopupMenuViewModel?> =
-        interactor.menu.map { model ->
-            model?.let {
-                KeyguardSettingsPopupMenuViewModel(
-                    icon =
-                        Icon.Resource(
-                            res = R.drawable.ic_settings,
-                            contentDescription = null,
-                        ),
-                    text =
-                        Text.Resource(
-                            res = R.string.lock_screen_settings,
-                        ),
-                    position = model.position,
-                    onClicked = model.onClicked,
-                    onDismissed = model.onDismissed,
-                )
-            }
-        }
-
     /** Notifies that the user has long-pressed on the lock screen. */
-    fun onLongPress(
-        x: Int,
-        y: Int,
-    ) {
-        interactor.onLongPress(
-            x = x,
-            y = y,
-        )
+    fun onLongPress() {
+        interactor.onLongPress()
+    }
+
+    /**
+     * Notifies that some input gesture has started somewhere outside of the lock screen settings
+     * menu item pop-up.
+     */
+    fun onTouchedOutside() {
+        interactor.onTouchedOutside()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
new file mode 100644
index 0000000..c36da9d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Models the UI state of a keyguard settings popup menu. */
+class KeyguardSettingsMenuViewModel
+@Inject
+constructor(
+    private val interactor: KeyguardLongPressInteractor,
+) {
+    val isVisible: Flow<Boolean> = interactor.isMenuVisible
+    val shouldOpenSettings: Flow<Boolean> = interactor.shouldOpenSettings
+
+    val icon: Icon =
+        Icon.Resource(
+            res = R.drawable.ic_palette,
+            contentDescription = null,
+        )
+
+    val text: Text =
+        Text.Resource(
+            res = R.string.lock_screen_settings,
+        )
+
+    fun onTouchGestureStarted() {
+        interactor.onMenuTouchGestureStarted()
+    }
+
+    fun onTouchGestureEnded(isClick: Boolean) {
+        interactor.onMenuTouchGestureEnded(
+            isClick = isClick,
+        )
+    }
+
+    fun onSettingsShown() {
+        interactor.onSettingsShown()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt
deleted file mode 100644
index 0571b05..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Position
-import com.android.systemui.common.shared.model.Text
-
-/** Models the UI state of a keyguard settings popup menu. */
-data class KeyguardSettingsPopupMenuViewModel(
-    val icon: Icon,
-    val text: Text,
-    /** Where the menu should be anchored, roughly in screen space. */
-    val position: Position,
-    /** Callback to invoke when the menu gets clicked by the user. */
-    val onClicked: () -> Unit,
-    /** Callback to invoke when the menu gets dismissed by the user. */
-    val onDismissed: () -> Unit,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 630f6b4..cffe45f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
@@ -392,6 +393,16 @@
     private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            StringBuilder extraComponentList = new StringBuilder(" components: ");
+            if (intent.hasExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST)) {
+                String[] comps = intent.getStringArrayExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST);
+                if (comps != null) {
+                    for (String c : comps) {
+                        extraComponentList.append(c).append(", ");
+                    }
+                }
+            }
+            Log.d(TAG_OPS, "launcherStateChanged intent: " + intent + extraComponentList);
             updateEnabledState();
 
             // Reconnect immediately, instead of waiting for resume to arrive.
@@ -402,9 +413,7 @@
     private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            if (SysUiState.DEBUG) {
-                Log.d(TAG_OPS, "Overview proxy service connected");
-            }
+            Log.d(TAG_OPS, "Overview proxy service connected");
             mConnectionBackoffAttempts = 0;
             mHandler.removeCallbacks(mDeferredConnectionCallback);
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 6f85c45..c9d1da38 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -289,7 +289,7 @@
         if (DEBUG_INPUT) {
             Log.d(TAG, "Predictive Back callback dispatched");
         }
-        respondToBack();
+        respondToKeyDismissal();
     };
 
     private ScreenshotView mScreenshotView;
@@ -581,7 +581,7 @@
         }
     }
 
-    private void respondToBack() {
+    private void respondToKeyDismissal() {
         dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
     }
 
@@ -641,11 +641,11 @@
         mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
 
         mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
                 if (DEBUG_INPUT) {
-                    Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK");
+                    Log.d(TAG, "onKeyEvent: " + keyCode);
                 }
-                respondToBack();
+                respondToKeyDismissal();
                 return true;
             }
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 72286f1..3711a2f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -162,7 +162,7 @@
     private fun registerUserSwitchObserver() {
         iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
             override fun onBeforeUserSwitching(newUserId: Int) {
-                setUserIdInternal(newUserId)
+                handleBeforeUserSwitching(newUserId)
             }
 
             override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -180,6 +180,10 @@
         }, TAG)
     }
 
+    protected open fun handleBeforeUserSwitching(newUserId: Int) {
+        setUserIdInternal(newUserId)
+    }
+
     @WorkerThread
     protected open fun handleUserSwitching(newUserId: Int) {
         Assert.isNotMainThread()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 79d3b26..3316ca0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -162,6 +162,8 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.ActivityStarter;
+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;
@@ -694,23 +696,29 @@
                     mInteractionJankMonitor.end(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
                 }
             };
+    private final ActivityStarter mActivityStarter;
 
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
             @Main Handler handler,
             LayoutInflater layoutInflater,
             FeatureFlags featureFlags,
-            NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
+            NotificationWakeUpCoordinator coordinator,
+            PulseExpansionHandler pulseExpansionHandler,
             DynamicPrivacyController dynamicPrivacyController,
-            KeyguardBypassController bypassController, FalsingManager falsingManager,
+            KeyguardBypassController bypassController,
+            FalsingManager falsingManager,
             FalsingCollector falsingCollector,
             KeyguardStateController keyguardStateController,
             StatusBarStateController statusBarStateController,
             StatusBarWindowStateController statusBarWindowStateController,
             NotificationShadeWindowController notificationShadeWindowController,
             DozeLog dozeLog,
-            DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
-            LatencyTracker latencyTracker, PowerManager powerManager,
+            DozeParameters dozeParameters,
+            CommandQueue commandQueue,
+            VibratorHelper vibratorHelper,
+            LatencyTracker latencyTracker,
+            PowerManager powerManager,
             AccessibilityManager accessibilityManager, @DisplayId int displayId,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             MetricsLogger metricsLogger,
@@ -771,7 +779,8 @@
             Provider<MultiShadeInteractor> multiShadeInteractorProvider,
             DumpManager dumpManager,
             KeyguardLongPressViewModel keyguardLongPressViewModel,
-            KeyguardInteractor keyguardInteractor) {
+            KeyguardInteractor keyguardInteractor,
+            ActivityStarter activityStarter) {
         mInteractionJankMonitor = interactionJankMonitor;
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
@@ -952,6 +961,7 @@
                     return Unit.INSTANCE;
                 },
                 mFalsingManager);
+        mActivityStarter = activityStarter;
         onFinishInflate();
         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
                 new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -1394,7 +1404,8 @@
                 mLockIconViewController,
                 stringResourceId ->
                         mKeyguardIndicationController.showTransientIndication(stringResourceId),
-                mVibratorHelper);
+                mVibratorHelper,
+                mActivityStarter);
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index b79f32a..b4653be 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -43,13 +43,13 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.qs.ChipVisibilityListener
 import com.android.systemui.qs.HeaderPrivacyIconsController
-import com.android.systemui.qs.carrier.QSCarrierGroup
-import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.shade.ShadeHeaderController.Companion.HEADER_TRANSITION_ID
 import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
 import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
 import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
 import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
+import com.android.systemui.shade.carrier.ShadeCarrierGroup
+import com.android.systemui.shade.carrier.ShadeCarrierGroupController
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusBarLocation
@@ -87,7 +87,7 @@
     private val variableDateViewControllerFactory: VariableDateViewController.Factory,
     @Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController,
     private val dumpManager: DumpManager,
-    private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
+    private val shadeCarrierGroupControllerBuilder: ShadeCarrierGroupController.Builder,
     private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
     private val demoModeController: DemoModeController,
     private val qsBatteryModeController: QsBatteryModeController,
@@ -114,13 +114,13 @@
 
     private lateinit var iconManager: StatusBarIconController.TintedIconManager
     private lateinit var carrierIconSlots: List<String>
-    private lateinit var qsCarrierGroupController: QSCarrierGroupController
+    private lateinit var mShadeCarrierGroupController: ShadeCarrierGroupController
 
     private val batteryIcon: BatteryMeterView = header.findViewById(R.id.batteryRemainingIcon)
     private val clock: Clock = header.findViewById(R.id.clock)
     private val date: TextView = header.findViewById(R.id.date)
     private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons)
-    private val qsCarrierGroup: QSCarrierGroup = header.findViewById(R.id.carrier_group)
+    private val mShadeCarrierGroup: ShadeCarrierGroup = header.findViewById(R.id.carrier_group)
 
     private var roundedCorners = 0
     private var cutout: DisplayCutout? = null
@@ -243,7 +243,7 @@
             override fun onDensityOrFontScaleChanged() {
                 clock.setTextAppearance(R.style.TextAppearance_QS_Status)
                 date.setTextAppearance(R.style.TextAppearance_QS_Status)
-                qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+                mShadeCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
                 loadConstraints()
                 header.minHeight =
                     resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
@@ -266,8 +266,8 @@
 
         carrierIconSlots =
             listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
-        qsCarrierGroupController =
-            qsCarrierGroupControllerBuilder.setQSCarrierGroup(qsCarrierGroup).build()
+        mShadeCarrierGroupController =
+            shadeCarrierGroupControllerBuilder.setShadeCarrierGroup(mShadeCarrierGroup).build()
 
         privacyIconsController.onParentVisible()
     }
@@ -284,7 +284,7 @@
             v.pivotX = newPivot
             v.pivotY = v.height.toFloat() / 2
 
-            qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
+            mShadeCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
         }
 
         dumpManager.registerDumpable(this)
@@ -439,12 +439,14 @@
     }
 
     private fun updateListeners() {
-        qsCarrierGroupController.setListening(visible)
+        mShadeCarrierGroupController.setListening(visible)
         if (visible) {
-            updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
-            qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
+            updateSingleCarrier(mShadeCarrierGroupController.isSingleCarrier)
+            mShadeCarrierGroupController.setOnSingleCarrierChangedListener {
+                updateSingleCarrier(it)
+            }
         } else {
-            qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
+            mShadeCarrierGroupController.setOnSingleCarrierChangedListener(null)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/shade/carrier/CellSignalState.kt
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
rename to packages/SystemUI/src/com/android/systemui/shade/carrier/CellSignalState.kt
index e925b54..958230b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/CellSignalState.kt
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier
+package com.android.systemui.shade.carrier
 
 /**
  * Represents the state of cell signal for a particular slot.
  *
- * To be used between [QSCarrierGroupController] and [QSCarrier].
+ * To be used between [ShadeCarrierGroupController] and [ShadeCarrier].
  */
 data class CellSignalState(
     @JvmField val visible: Boolean = false,
@@ -37,7 +37,6 @@
      * @return `this` if `this.visible == visible`. Else, a new copy with the visibility changed.
      */
     fun changeVisibility(visible: Boolean): CellSignalState {
-        if (this.visible == visible) return this
-        else return copy(visible = visible)
+        if (this.visible == visible) return this else return copy(visible = visible)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
rename to packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
index b5ceeae..8586828 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier;
+package com.android.systemui.shade.carrier;
 
 import android.annotation.StyleRes;
 import android.content.Context;
@@ -38,7 +38,7 @@
 
 import java.util.Objects;
 
-public class QSCarrier extends LinearLayout {
+public class ShadeCarrier extends LinearLayout {
 
     private View mMobileGroup;
     private TextView mCarrierText;
@@ -50,19 +50,19 @@
     private boolean mMobileSignalInitialized = false;
     private boolean mIsSingleCarrier;
 
-    public QSCarrier(Context context) {
+    public ShadeCarrier(Context context) {
         super(context);
     }
 
-    public QSCarrier(Context context, AttributeSet attrs) {
+    public ShadeCarrier(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public QSCarrier(Context context, AttributeSet attrs, int defStyleAttr) {
+    public ShadeCarrier(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
-    public QSCarrier(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public ShadeCarrier(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
@@ -72,7 +72,7 @@
         mMobileGroup = findViewById(R.id.mobile_combo);
         mMobileRoaming = findViewById(R.id.mobile_roaming);
         mMobileSignal = findViewById(R.id.mobile_signal);
-        mCarrierText = findViewById(R.id.qs_carrier_text);
+        mCarrierText = findViewById(R.id.shade_carrier_text);
         mSpacer = findViewById(R.id.spacer);
         updateResources();
     }
@@ -158,7 +158,7 @@
         mCarrierText.setMaxEms(
                 useLargeScreenHeader
                         ? Integer.MAX_VALUE
-                        : getResources().getInteger(R.integer.qs_carrier_max_em)
+                        : getResources().getInteger(R.integer.shade_carrier_max_em)
         );
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
rename to packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
index a36035b..68561d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroup.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier;
+package com.android.systemui.shade.carrier;
 
 import android.annotation.StyleRes;
 import android.content.Context;
@@ -27,10 +27,10 @@
 import com.android.systemui.R;
 
 /**
- * Displays Carrier name and network status in QS
+ * Displays Carrier name and network status in the shade header
  */
-public class QSCarrierGroup extends LinearLayout {
-    public QSCarrierGroup(Context context, AttributeSet attrs) {
+public class ShadeCarrierGroup extends LinearLayout {
+    public ShadeCarrierGroup(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
@@ -38,24 +38,24 @@
         return findViewById(R.id.no_carrier_text);
     }
 
-    QSCarrier getCarrier1View() {
+    ShadeCarrier getCarrier1View() {
         return findViewById(R.id.carrier1);
     }
 
-    QSCarrier getCarrier2View() {
+    ShadeCarrier getCarrier2View() {
         return findViewById(R.id.carrier2);
     }
 
-    QSCarrier getCarrier3View() {
+    ShadeCarrier getCarrier3View() {
         return findViewById(R.id.carrier3);
     }
 
     View getCarrierDivider1() {
-        return findViewById(R.id.qs_carrier_divider1);
+        return findViewById(R.id.shade_carrier_divider1);
     }
 
     View getCarrierDivider2() {
-        return findViewById(R.id.qs_carrier_divider2);
+        return findViewById(R.id.shade_carrier_divider2);
     }
 
     public void updateTextAppearance(@StyleRes int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
rename to packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 6a8bf75..0ebcfa2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier;
+package com.android.systemui.shade.carrier;
 
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
 
@@ -52,8 +52,8 @@
 
 import javax.inject.Inject;
 
-public class QSCarrierGroupController {
-    private static final String TAG = "QSCarrierGroup";
+public class ShadeCarrierGroupController {
+    private static final String TAG = "ShadeCarrierGroup";
 
     /**
      * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
@@ -72,7 +72,7 @@
     private final CellSignalState[] mInfos =
             new CellSignalState[SIM_SLOTS];
     private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
-    private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
+    private ShadeCarrier[] mCarrierGroups = new ShadeCarrier[SIM_SLOTS];
     private int[] mLastSignalLevel = new int[SIM_SLOTS];
     private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
     private final CarrierConfigTracker mCarrierConfigTracker;
@@ -129,7 +129,7 @@
         }
     }
 
-    private QSCarrierGroupController(QSCarrierGroup view, ActivityStarter activityStarter,
+    private ShadeCarrierGroupController(ShadeCarrierGroup view, ActivityStarter activityStarter,
             @Background Handler bgHandler, @Main Looper mainLooper,
             NetworkController networkController,
             CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
@@ -167,7 +167,7 @@
         for (int i = 0; i < SIM_SLOTS; i++) {
             mInfos[i] = new CellSignalState(
                     true,
-                    R.drawable.ic_qs_no_calling_sms,
+                    R.drawable.ic_shade_no_calling_sms,
                     context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
                     "",
                     false);
@@ -257,7 +257,7 @@
         if (singleCarrier) {
             for (int i = 0; i < SIM_SLOTS; i++) {
                 if (mInfos[i].visible
-                        && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
+                        && mInfos[i].mobileSignalIconId == R.drawable.ic_shade_sim_card) {
                     mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
                 }
             }
@@ -322,8 +322,8 @@
                 Log.e(TAG, "Carrier information arrays not of same length");
             }
         } else {
-            // No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
-            // info.carrierText in a different view.
+            // No sims or airplane mode (but not WFC). Do not show ShadeCarrierGroup,
+            // instead just show info.carrierText in a different view.
             for (int i = 0; i < SIM_SLOTS; i++) {
                 mInfos[i] = mInfos[i].changeVisibility(false);
                 mCarrierGroups[i].setCarrierText("");
@@ -368,7 +368,7 @@
     }
 
     public static class Builder {
-        private QSCarrierGroup mView;
+        private ShadeCarrierGroup mView;
         private final ActivityStarter mActivityStarter;
         private final Handler mHandler;
         private final Looper mLooper;
@@ -393,13 +393,13 @@
             mSlotIndexResolver = slotIndexResolver;
         }
 
-        public Builder setQSCarrierGroup(QSCarrierGroup view) {
+        public Builder setShadeCarrierGroup(ShadeCarrierGroup view) {
             mView = view;
             return this;
         }
 
-        public QSCarrierGroupController build() {
-            return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
+        public ShadeCarrierGroupController build() {
+            return new ShadeCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
                     mNetworkController, mCarrierTextControllerBuilder, mContext,
                     mCarrierConfigTracker, mSlotIndexResolver);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 565c0a9..34300c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -38,8 +38,8 @@
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.carrier.QSCarrierGroupController;
 import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
 import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.MediaArtworkProcessor;
@@ -78,14 +78,14 @@
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
 import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
 /**
  * This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
  * this separate from {@link CentralSurfacesModule} module so that components that wish to build
@@ -271,8 +271,8 @@
 
     /** */
     @Binds
-    QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
-            QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
+    ShadeCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
+            ShadeCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
 
     /**
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 15ad312..1631ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.dagger.PeopleHeader
 import com.android.systemui.statusbar.notification.icon.ConversationIconManager
@@ -40,27 +41,28 @@
  */
 @CoordinatorScope
 class ConversationCoordinator @Inject constructor(
-    private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
-    private val conversationIconManager: ConversationIconManager,
-    @PeopleHeader peopleHeaderController: NodeController
+        private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+        private val conversationIconManager: ConversationIconManager,
+        private val highPriorityProvider: HighPriorityProvider,
+        @PeopleHeader private val peopleHeaderController: NodeController,
 ) : Coordinator {
 
     private val promotedEntriesToSummaryOfSameChannel =
-        mutableMapOf<NotificationEntry, NotificationEntry>()
+            mutableMapOf<NotificationEntry, NotificationEntry>()
 
     private val onBeforeRenderListListener = OnBeforeRenderListListener { _ ->
         val unimportantSummaries = promotedEntriesToSummaryOfSameChannel
-            .mapNotNull { (promoted, summary) ->
-                val originalGroup = summary.parent
-                when {
-                    originalGroup == null -> null
-                    originalGroup == promoted.parent -> null
-                    originalGroup.parent == null -> null
-                    originalGroup.summary != summary -> null
-                    originalGroup.children.any { it.channel == summary.channel } -> null
-                    else -> summary.key
+                .mapNotNull { (promoted, summary) ->
+                    val originalGroup = summary.parent
+                    when {
+                        originalGroup == null -> null
+                        originalGroup == promoted.parent -> null
+                        originalGroup.parent == null -> null
+                        originalGroup.summary != summary -> null
+                        originalGroup.children.any { it.channel == summary.channel } -> null
+                        else -> summary.key
+                    }
                 }
-            }
         conversationIconManager.setUnimportantConversations(unimportantSummaries)
         promotedEntriesToSummaryOfSameChannel.clear()
     }
@@ -78,21 +80,23 @@
         }
     }
 
-    val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) {
+    val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
         override fun isInSection(entry: ListEntry): Boolean =
-                isConversation(entry)
+               highPriorityProvider.isHighPriorityConversation(entry)
 
-        override fun getComparator() = object : NotifComparator("People") {
-            override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
-                val type1 = getPeopleType(entry1)
-                val type2 = getPeopleType(entry2)
-                return type2.compareTo(type1)
-            }
-        }
+        override fun getComparator(): NotifComparator = notifComparator
 
-        override fun getHeaderNodeController() =
-                // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
-                if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
+        override fun getHeaderNodeController(): NodeController? = conversationHeaderNodeController
+    }
+
+    val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) {
+        // Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting.
+        // All remaining conversations must be silent.
+        override fun isInSection(entry: ListEntry): Boolean = isConversation(entry)
+
+        override fun getComparator(): NotifComparator = notifComparator
+
+        override fun getHeaderNodeController(): NodeController? = conversationHeaderNodeController
     }
 
     override fun attach(pipeline: NotifPipeline) {
@@ -101,15 +105,27 @@
     }
 
     private fun isConversation(entry: ListEntry): Boolean =
-        getPeopleType(entry) != TYPE_NON_PERSON
+            getPeopleType(entry) != TYPE_NON_PERSON
 
     @PeopleNotificationType
     private fun getPeopleType(entry: ListEntry): Int =
-        entry.representativeEntry?.let {
-            peopleNotificationIdentifier.getPeopleNotificationType(it)
-        } ?: TYPE_NON_PERSON
+            entry.representativeEntry?.let {
+                peopleNotificationIdentifier.getPeopleNotificationType(it)
+            } ?: TYPE_NON_PERSON
 
-    companion object {
+    private val notifComparator: NotifComparator = object : NotifComparator("People") {
+        override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+            val type1 = getPeopleType(entry1)
+            val type2 = getPeopleType(entry2)
+            return type2.compareTo(type1)
+        }
+    }
+
+    // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
+    private val conversationHeaderNodeController: NodeController? =
+            if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
+
+    private companion object {
         private const val TAG = "ConversationCoordinator"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 6bb5b92..02ce0d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
 import javax.inject.Inject
 
 /**
@@ -32,6 +33,7 @@
 @CoordinatorScope
 class NotifCoordinatorsImpl @Inject constructor(
         notifPipelineFlags: NotifPipelineFlags,
+        sectionStyleProvider: SectionStyleProvider,
         dataStoreCoordinator: DataStoreCoordinator,
         hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
         hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
@@ -56,7 +58,7 @@
         viewConfigCoordinator: ViewConfigCoordinator,
         visualStabilityCoordinator: VisualStabilityCoordinator,
         sensitiveContentCoordinator: SensitiveContentCoordinator,
-        dismissibilityCoordinator: DismissibilityCoordinator
+        dismissibilityCoordinator: DismissibilityCoordinator,
 ) : NotifCoordinators {
 
     private val mCoordinators: MutableList<Coordinator> = ArrayList()
@@ -99,13 +101,20 @@
         mCoordinators.add(dismissibilityCoordinator)
 
         // Manually add Ordered Sections
-        // HeadsUp > FGS > People > Alerting > Silent > Minimized > Unknown/Default
-        mOrderedSections.add(headsUpCoordinator.sectioner)
+        mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
         mOrderedSections.add(appOpsCoordinator.sectioner) // ForegroundService
-        mOrderedSections.add(conversationCoordinator.sectioner) // People
+        mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting
+        mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent
         mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
         mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
         mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
+
+        sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner))
+        sectionStyleProvider.setSilentSections(listOf(
+                conversationCoordinator.peopleSilentSectioner,
+                rankingCoordinator.silentSectioner,
+                rankingCoordinator.minimizedSectioner,
+        ))
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index ea5cb30..1d37dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -27,15 +27,12 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
 import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
 
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -52,7 +49,6 @@
     public static final boolean SHOW_ALL_SECTIONS = false;
     private final StatusBarStateController mStatusBarStateController;
     private final HighPriorityProvider mHighPriorityProvider;
-    private final SectionStyleProvider mSectionStyleProvider;
     private final NodeController mSilentNodeController;
     private final SectionHeaderController mSilentHeaderController;
     private final NodeController mAlertingHeaderController;
@@ -63,13 +59,11 @@
     public RankingCoordinator(
             StatusBarStateController statusBarStateController,
             HighPriorityProvider highPriorityProvider,
-            SectionStyleProvider sectionStyleProvider,
             @AlertingHeader NodeController alertingHeaderController,
             @SilentHeader SectionHeaderController silentHeaderController,
             @SilentHeader NodeController silentNodeController) {
         mStatusBarStateController = statusBarStateController;
         mHighPriorityProvider = highPriorityProvider;
-        mSectionStyleProvider = sectionStyleProvider;
         mAlertingHeaderController = alertingHeaderController;
         mSilentNodeController = silentNodeController;
         mSilentHeaderController = silentHeaderController;
@@ -78,9 +72,6 @@
     @Override
     public void attach(NotifPipeline pipeline) {
         mStatusBarStateController.addCallback(mStatusBarStateCallback);
-        mSectionStyleProvider.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner));
-        mSectionStyleProvider.setSilentSections(
-                Arrays.asList(mSilentNotifSectioner, mMinimizedNotifSectioner));
 
         pipeline.addPreGroupFilter(mSuspendedFilter);
         pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index e7ef2ec..731ec80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.notification.collection.provider;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationManager;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -63,7 +66,7 @@
      * A GroupEntry is considered high priority if its representativeEntry (summary) or children are
      * high priority
      */
-    public boolean isHighPriority(ListEntry entry) {
+    public boolean isHighPriority(@Nullable ListEntry entry) {
         if (entry == null) {
             return false;
         }
@@ -78,6 +81,36 @@
                 || hasHighPriorityChild(entry);
     }
 
+    /**
+     * @return true if the ListEntry is high priority conversation, else false
+     */
+    public boolean isHighPriorityConversation(@NonNull ListEntry entry) {
+        final NotificationEntry notifEntry = entry.getRepresentativeEntry();
+        if (notifEntry == null) {
+            return  false;
+        }
+
+        if (!isPeopleNotification(notifEntry)) {
+            return false;
+        }
+
+        if (notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT) {
+            return true;
+        }
+
+        return isNotificationEntryWithAtLeastOneImportantChild(entry);
+    }
+
+    private boolean isNotificationEntryWithAtLeastOneImportantChild(@NonNull ListEntry entry) {
+        if (!(entry instanceof GroupEntry)) {
+            return false;
+        }
+        final GroupEntry groupEntry = (GroupEntry) entry;
+        return groupEntry.getChildren().stream().anyMatch(
+                childEntry ->
+                        childEntry.getRanking().getImportance()
+                                >= NotificationManager.IMPORTANCE_DEFAULT);
+    }
 
     private boolean hasHighPriorityChild(ListEntry entry) {
         if (entry instanceof NotificationEntry
@@ -93,7 +126,6 @@
                 }
             }
         }
-
         return false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
new file mode 100644
index 0000000..f2216fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.interruption
+
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
+
+/**
+ * Wraps a [NotificationInterruptStateProvider] to convert it to the new
+ * [VisualInterruptionDecisionProvider] interface.
+ */
+@SysUISingleton
+class NotificationInterruptStateProviderWrapper(
+    private val wrapped: NotificationInterruptStateProvider
+) : VisualInterruptionDecisionProvider {
+
+    @VisibleForTesting
+    enum class DecisionImpl(override val shouldInterrupt: Boolean) : Decision {
+        SHOULD_INTERRUPT(shouldInterrupt = true),
+        SHOULD_NOT_INTERRUPT(shouldInterrupt = false);
+
+        companion object {
+            fun of(booleanDecision: Boolean) =
+                if (booleanDecision) SHOULD_INTERRUPT else SHOULD_NOT_INTERRUPT
+        }
+    }
+
+    @VisibleForTesting
+    class FullScreenIntentDecisionImpl(
+        val originalEntry: NotificationEntry,
+        val originalDecision: NotificationInterruptStateProvider.FullScreenIntentDecision
+    ) : FullScreenIntentDecision {
+        override val shouldInterrupt = originalDecision.shouldLaunch
+        override val wouldInterruptWithoutDnd = originalDecision == NO_FSI_SUPPRESSED_ONLY_BY_DND
+    }
+
+    override fun addSuppressor(suppressor: NotificationInterruptSuppressor) {
+        wrapped.addSuppressor(suppressor)
+    }
+
+    override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision =
+        wrapped.checkHeadsUp(entry, /* log= */ false).let { DecisionImpl.of(it) }
+
+    override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision =
+        wrapped.checkHeadsUp(entry, /* log= */ true).let { DecisionImpl.of(it) }
+
+    override fun makeUnloggedFullScreenIntentDecision(entry: NotificationEntry) =
+        wrapped.getFullScreenIntentDecision(entry).let { FullScreenIntentDecisionImpl(entry, it) }
+
+    override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) {
+        (decision as FullScreenIntentDecisionImpl).let {
+            wrapped.logFullScreenIntentDecision(it.originalEntry, it.originalDecision)
+        }
+    }
+
+    override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision =
+        wrapped.shouldBubbleUp(entry).let { DecisionImpl.of(it) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
new file mode 100644
index 0000000..c0f4fcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.interruption
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * Decides whether a notification should visually interrupt the user in various ways.
+ *
+ * These include displaying the notification as heads-up (peeking while the device is awake or
+ * pulsing while the device is dozing), displaying the notification as a bubble, and launching a
+ * full-screen intent for the notification.
+ */
+interface VisualInterruptionDecisionProvider {
+    /**
+     * Represents the decision to visually interrupt or not.
+     *
+     * Used for heads-up and bubble decisions; subclassed by [FullScreenIntentDecision] for
+     * full-screen intent decisions.
+     *
+     * @property[shouldInterrupt] whether a visual interruption should be triggered
+     */
+    interface Decision {
+        val shouldInterrupt: Boolean
+    }
+
+    /**
+     * Represents the decision to launch a full-screen intent for a notification or not.
+     *
+     * @property[wouldInterruptWithoutDnd] whether a full-screen intent should not be launched only
+     *   because Do Not Disturb has suppressed it
+     */
+    interface FullScreenIntentDecision : Decision {
+        val wouldInterruptWithoutDnd: Boolean
+    }
+
+    /**
+     * Adds a [component][suppressor] that can suppress visual interruptions.
+     *
+     * This class may call suppressors in any order.
+     *
+     * @param[suppressor] the suppressor to add
+     */
+    fun addSuppressor(suppressor: NotificationInterruptSuppressor)
+
+    /**
+     * Decides whether a [notification][entry] should display as heads-up or not, but does not log
+     * that decision.
+     *
+     * @param[entry] the notification that this decision is about
+     * @return the decision to display that notification as heads-up or not
+     */
+    fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision
+
+    /**
+     * Decides whether a [notification][entry] should display as heads-up or not, and logs that
+     * decision.
+     *
+     * If the device is awake, the decision will consider whether the notification should "peek"
+     * (slide in from the top of the screen over the current activity).
+     *
+     * If the device is dozing, the decision will consider whether the notification should "pulse"
+     * (wake the screen up and display the ambient view of the notification).
+     *
+     * @see[makeUnloggedHeadsUpDecision]
+     *
+     * @param[entry] the notification that this decision is about
+     * @return the decision to display that notification as heads-up or not
+     */
+    fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision
+
+    /**
+     * Decides whether a [notification][entry] should launch a full-screen intent or not, but does
+     * not log that decision.
+     *
+     * The returned decision can be logged by passing it to [logFullScreenIntentDecision].
+     *
+     * @see[makeAndLogHeadsUpDecision]
+     *
+     * @param[entry] the notification that this decision is about
+     * @return the decision to launch a full-screen intent for that notification or not
+     */
+    fun makeUnloggedFullScreenIntentDecision(entry: NotificationEntry): FullScreenIntentDecision
+
+    /**
+     * Logs a previous [decision] to launch a full-screen intent or not.
+     *
+     * @param[decision] the decision to log
+     */
+    fun logFullScreenIntentDecision(decision: FullScreenIntentDecision)
+
+    /**
+     * Decides whether a [notification][entry] should display as a bubble or not.
+     *
+     * @param[entry] the notification that this decision is about
+     * @return the decision to display that notification as a bubble or not
+     */
+    fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index e4227dc..d433814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 
@@ -57,7 +58,7 @@
     }
 
     private var ambientIndicationArea: View? = null
-    private lateinit var binding: KeyguardBottomAreaViewBinder.Binding
+    private var binding: KeyguardBottomAreaViewBinder.Binding? = null
     private var lockIconViewController: LockIconViewController? = null
 
     /** Initializes the view. */
@@ -67,13 +68,16 @@
         lockIconViewController: LockIconViewController? = null,
         messageDisplayer: MessageDisplayer? = null,
         vibratorHelper: VibratorHelper? = null,
+        activityStarter: ActivityStarter? = null,
     ) {
+        binding?.destroy()
         binding =
             bind(
                 this,
                 viewModel,
                 falsingManager,
                 vibratorHelper,
+                activityStarter,
             ) {
                 messageDisplayer?.display(it)
             }
@@ -114,12 +118,12 @@
 
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
-        binding.onConfigurationChanged()
+        binding?.onConfigurationChanged()
     }
 
     /** Returns a list of animators to use to animate the indication areas. */
     val indicationAreaAnimators: List<ViewPropertyAnimator>
-        get() = binding.getIndicationAreaAnimators()
+        get() = checkNotNull(binding).getIndicationAreaAnimators()
 
     override fun hasOverlappingRendering(): Boolean {
         return false
@@ -139,7 +143,7 @@
         super.onLayout(changed, left, top, right, bottom)
         findViewById<View>(R.id.ambient_indication_container)?.let {
             val (ambientLeft, ambientTop) = it.locationOnScreen
-            if (binding.shouldConstrainToTopOfLockIcon()) {
+            if (binding?.shouldConstrainToTopOfLockIcon() == true) {
                 // make top of ambient indication view the bottom of the lock icon
                 it.layout(
                     ambientLeft,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index b303151..c817466 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -85,17 +85,16 @@
 
     /**
      * Called when keyguard is about to be displayed and allows to perform custom animation
-     *
-     * @return A handle that can be used for cancelling the animation, if necessary
      */
-    fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
-        animations.forEach {
+    fun animateInKeyguard(keyguardView: View, after: Runnable) =
+        animations.firstOrNull {
             if (it.shouldAnimateInKeyguard()) {
-                return@animateInKeyguard it.animateInKeyguard(keyguardView, after)
+                it.animateInKeyguard(keyguardView, after)
+                true
+            } else {
+                false
             }
         }
-        return null
-    }
 
     /**
      * If returns true it will disable propagating touches to apps and keyguard
@@ -212,10 +211,7 @@
     fun onAlwaysOnChanged(alwaysOn: Boolean) {}
 
     fun shouldAnimateInKeyguard(): Boolean = false
-    fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
-        after.run()
-        return null
-    }
+    fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
 
     fun shouldDelayKeyguardShow(): Boolean = false
     fun isKeyguardShowDelayed(): Boolean = false
@@ -228,7 +224,3 @@
     fun shouldAnimateDozingChange(): Boolean = true
     fun shouldAnimateClockChange(): Boolean = true
 }
-
-interface AnimatorHandle {
-    fun cancel()
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index de7bf3c..d731f88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -230,7 +230,7 @@
         if (state == null) {
             return;
         }
-        if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
+        if (statusIcon.icon == R.drawable.ic_shade_no_calling_sms) {
             state.isNoCalling = statusIcon.visible;
             state.noCallingDescription = statusIcon.contentDescription;
         } else {
@@ -422,7 +422,7 @@
 
         private CallIndicatorIconState(int subId) {
             this.subId = subId;
-            this.noCallingResId = R.drawable.ic_qs_no_calling_sms;
+            this.noCallingResId = R.drawable.ic_shade_no_calling_sms;
             this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index deb0414..118bfc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -160,7 +160,7 @@
      * Animates in the provided keyguard view, ending in the same position that it will be in on
      * AOD.
      */
-    override fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle {
+    override fun animateInKeyguard(keyguardView: View, after: Runnable) {
         shouldAnimateInKeyguard = false
         keyguardView.alpha = 0f
         keyguardView.visibility = View.VISIBLE
@@ -175,36 +175,11 @@
         // We animate the Y properly separately using the PropertyAnimator, as the panel
         // view also needs to update the end position.
         PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y)
+        PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY,
+                AnimationProperties().setDuration(duration.toLong()),
+                true /* animate */)
 
-        // Start the animation on the next frame using Choreographer APIs. animateInKeyguard() is
-        // called while the system is busy processing lots of requests, so delaying the animation a
-        // frame will mitigate jank. In the event the animation is cancelled before the next frame
-        // is called, this callback will be removed
-        val keyguardAnimator = keyguardView.animate()
-        val nextFrameCallback = TraceUtils.namedRunnable("startAnimateInKeyguard") {
-            PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY,
-                    AnimationProperties().setDuration(duration.toLong()),
-                    true /* animate */)
-            keyguardAnimator.start()
-        }
-        DejankUtils.postAfterTraversal(nextFrameCallback)
-        val animatorHandle = object : AnimatorHandle {
-            private var hasCancelled = false
-            override fun cancel() {
-                if (!hasCancelled) {
-                    DejankUtils.removeCallbacks(nextFrameCallback)
-                    // If we're cancelled, reset state flags/listeners. The end action above
-                    // will not be called, which is what we want since that will finish the
-                    // screen off animation and show the lockscreen, which we don't want if we
-                    // were cancelled.
-                    aodUiAnimationPlaying = false
-                    decidedToAnimateGoingToSleep = null
-                    keyguardView.animate().setListener(null)
-                    hasCancelled = true
-                }
-            }
-        }
-        keyguardAnimator
+        keyguardView.animate()
                 .setDuration(duration.toLong())
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(1f)
@@ -230,7 +205,14 @@
                 }
                 .setListener(object : AnimatorListenerAdapter() {
                     override fun onAnimationCancel(animation: Animator?) {
-                        animatorHandle.cancel()
+                        // If we're cancelled, reset state flags/listeners. The end action above
+                        // will not be called, which is what we want since that will finish the
+                        // screen off animation and show the lockscreen, which we don't want if we
+                        // were cancelled.
+                        aodUiAnimationPlaying = false
+                        decidedToAnimateGoingToSleep = null
+                        keyguardView.animate().setListener(null)
+
                         interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
                     }
 
@@ -240,7 +222,7 @@
                                 CUJ_SCREEN_OFF_SHOW_AOD)
                     }
                 })
-        return animatorHandle
+                .start()
     }
 
     override fun onStartedWakingUp() {
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 dce7bf2..bfd133e 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
@@ -37,7 +37,6 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 
 /** Common interface for all of the location-based mobile icon view models. */
@@ -80,7 +79,12 @@
 ) : MobileIconViewModelCommon {
     /** Whether or not to show the error state of [SignalDrawable] */
     private val showExclamationMark: Flow<Boolean> =
-        iconInteractor.isDefaultDataEnabled.mapLatest { !it }
+        combine(
+            iconInteractor.isDefaultDataEnabled,
+            iconInteractor.isDefaultConnectionFailed,
+        ) { isDefaultDataEnabled, isDefaultConnectionFailed ->
+            !isDefaultDataEnabled || isDefaultConnectionFailed
+        }
 
     override val isVisible: StateFlow<Boolean> =
         if (!constants.hasDataCapabilities) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index edee3f1..64c028e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1017,7 +1017,7 @@
             // THEN the display should be unconfigured once. If the timeout action is not
             // cancelled, the display would be unconfigured twice which would cause two
             // FP attempts.
-            verify(mUdfpsView, times(1)).unconfigureDisplay();
+            verify(mUdfpsView).unconfigureDisplay();
         } else {
             verify(mUdfpsView, never()).unconfigureDisplay();
         }
@@ -1301,8 +1301,8 @@
         mBiometricExecutor.runAllReady();
         downEvent.recycle();
 
-        // THEN the touch is pilfered, expected twice (valid overlap and touch on sensor)
-        verify(mInputManager, times(2)).pilferPointers(any());
+        // THEN the touch is pilfered
+        verify(mInputManager).pilferPointers(any());
     }
 
     @Test
@@ -1340,7 +1340,7 @@
         downEvent.recycle();
 
         // THEN the touch is NOT pilfered
-        verify(mInputManager, times(0)).pilferPointers(any());
+        verify(mInputManager, never()).pilferPointers(any());
     }
 
     @Test
@@ -1380,7 +1380,51 @@
         downEvent.recycle();
 
         // THEN the touch is pilfered
-        verify(mInputManager, times(1)).pilferPointers(any());
+        verify(mInputManager).pilferPointers(any());
+    }
+
+    @Test
+    public void onTouch_withNewTouchDetection_doNotPilferWhenPullingUpBouncer()
+            throws RemoteException {
+        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+                0L);
+        final TouchProcessorResult processorResultMove =
+                new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+                        1 /* pointerId */, touchData);
+
+        // Enable new touch detection.
+        when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+        // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+        initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+        // Configure UdfpsView to accept the ACTION_MOVE event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+        // GIVEN that the alternate bouncer is not showing and a11y touch exploration NOT enabled
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+        // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS
+        when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
+        when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f);
+
+        // WHEN ACTION_MOVE is received and touch is within sensor
+        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+                processorResultMove);
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        mBiometricExecutor.runAllReady();
+        moveEvent.recycle();
+
+        // THEN the touch is NOT pilfered
+        verify(mInputManager, never()).pilferPointers(any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
index 5fcf414..8fdc491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -29,23 +29,45 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Collection;
 import java.util.HashSet;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
+
+    private FakeExecutor mExecutor;
+    private DreamOverlayStateController mStateController;
+    private ComplicationCollectionLiveData mLiveData;
+    private FakeFeatureFlags mFeatureFlags;
+    @Mock
+    private Observer mObserver;
+
     @Before
-    public void setUp() throws Exception {
-        allowTestableLooperAsMainThread();
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mFeatureFlags = new FakeFeatureFlags();
+        mExecutor = new FakeExecutor(new FakeSystemClock());
+        mFeatureFlags.set(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS, true);
+        mStateController = new DreamOverlayStateController(
+                mExecutor,
+                /* overlayEnabled= */ true,
+                mFeatureFlags);
+        mLiveData = new ComplicationCollectionLiveData(mStateController);
     }
 
     @Test
@@ -53,45 +75,41 @@
      * Ensures registration and callback lifecycles are respected.
      */
     public void testLifecycle() {
-        getContext().getMainExecutor().execute(() -> {
-            final DreamOverlayStateController stateController =
-                    Mockito.mock(DreamOverlayStateController.class);
-            final ComplicationCollectionLiveData liveData =
-                    new ComplicationCollectionLiveData(stateController);
-            final HashSet<Complication> complications = new HashSet<>();
-            final Observer<Collection<Complication>> observer = Mockito.mock(Observer.class);
-            complications.add(Mockito.mock(Complication.class));
+        final HashSet<Complication> complications = new HashSet<>();
+        mLiveData.observeForever(mObserver);
+        mExecutor.runAllReady();
+        // Verify observer called with empty complications
+        assertObserverCalledWith(complications);
 
-            when(stateController.getComplications()).thenReturn(complications);
+        addComplication(mock(Complication.class), complications);
+        assertObserverCalledWith(complications);
 
-            liveData.observeForever(observer);
-            ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
-                    ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+        addComplication(mock(Complication.class), complications);
+        assertObserverCalledWith(complications);
 
-            verify(stateController).addCallback(callbackCaptor.capture());
-            verifyUpdate(observer, complications);
-
-            complications.add(Mockito.mock(Complication.class));
-            callbackCaptor.getValue().onComplicationsChanged();
-
-            verifyUpdate(observer, complications);
-
-            callbackCaptor.getValue().onAvailableComplicationTypesChanged();
-
-            verifyUpdate(observer, complications);
-        });
+        mStateController.setAvailableComplicationTypes(0);
+        mExecutor.runAllReady();
+        assertObserverCalledWith(complications);
+        mLiveData.removeObserver(mObserver);
     }
 
-    void verifyUpdate(Observer<Collection<Complication>> observer,
-            Collection<Complication> targetCollection) {
+    private void assertObserverCalledWith(Collection<Complication> targetCollection) {
         ArgumentCaptor<Collection<Complication>> collectionCaptor =
                 ArgumentCaptor.forClass(Collection.class);
 
-        verify(observer).onChanged(collectionCaptor.capture());
+        verify(mObserver).onChanged(collectionCaptor.capture());
 
-        final Collection collection =  collectionCaptor.getValue();
+        final Collection<Complication> collection = collectionCaptor.getValue();
+
         assertThat(collection.containsAll(targetCollection)
                 && targetCollection.containsAll(collection)).isTrue();
-        Mockito.clearInvocations(observer);
+        Mockito.clearInvocations(mObserver);
+    }
+
+    private void addComplication(Complication complication,
+            Collection<Complication> complications) {
+        complications.add(complication);
+        mStateController.addComplication(complication);
+        mExecutor.runAllReady();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index c93e677..0de9608 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -66,6 +66,8 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
@@ -142,6 +144,8 @@
 
     private @Mock CentralSurfaces mCentralSurfaces;
 
+    private FakeFeatureFlags mFeatureFlags;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -160,6 +164,8 @@
                 mColorExtractor, mDumpManager, mKeyguardStateController,
                 mScreenOffAnimationController, mAuthController, mShadeExpansionStateManager,
                 mShadeWindowLogger);
+        mFeatureFlags = new FakeFeatureFlags();
+
 
         DejankUtils.setImmediate(true);
 
@@ -515,6 +521,28 @@
         verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean());
     }
 
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testNotStartingKeyguardWhenFlagIsDisabled() {
+        mViewMediator.setShowingLocked(false);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+        mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false);
+        mViewMediator.onDreamingStarted();
+        assertFalse(mViewMediator.isShowingAndNotOccluded());
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testStartingKeyguardWhenFlagIsEnabled() {
+        mViewMediator.setShowingLocked(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, true);
+        mViewMediator.onDreamingStarted();
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+    }
+
     private void createAndStartViewMediator() {
         mViewMediator = new KeyguardViewMediator(
                 mContext,
@@ -545,7 +573,8 @@
                 () -> mShadeController,
                 () -> mNotificationShadeWindowController,
                 () -> mActivityLaunchAnimator,
-                () -> mScrimController);
+                () -> mScrimController,
+                mFeatureFlags);
         mViewMediator.start();
 
         mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 86e8c9a..a668af3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
+import java.util.Locale
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -67,6 +68,7 @@
 
     @Before
     fun setUp() {
+        context.resources.configuration.setLayoutDirection(Locale.US)
         config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
         config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
         val testDispatcher = StandardTestDispatcher()
@@ -222,6 +224,40 @@
     }
 
     @Test
+    fun getSlotPickerRepresentations_rightToLeft_slotsReversed() {
+        context.resources.configuration.setLayoutDirection(Locale("he", "IL"))
+        val slot1 = "slot1"
+        val slot2 = "slot2"
+        val slot3 = "slot3"
+        context.orCreateTestableResources.addOverride(
+            R.array.config_keyguardQuickAffordanceSlots,
+            arrayOf(
+                "$slot1:2",
+                "$slot2:4",
+                "$slot3:5",
+            ),
+        )
+
+        assertThat(underTest.getSlotPickerRepresentations())
+            .isEqualTo(
+                listOf(
+                    KeyguardSlotPickerRepresentation(
+                        id = slot3,
+                        maxSelectedAffordances = 5,
+                    ),
+                    KeyguardSlotPickerRepresentation(
+                        id = slot2,
+                        maxSelectedAffordances = 4,
+                    ),
+                    KeyguardSlotPickerRepresentation(
+                        id = slot1,
+                        maxSelectedAffordances = 2,
+                    ),
+                )
+            )
+    }
+
+    @Test
     fun `selections for secondary user`() =
         testScope.runTest {
             userTracker.set(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 51988ef..77bb12c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -29,20 +29,20 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.util.mockito.any
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -51,8 +51,8 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardLongPressInteractorTest : SysuiTestCase() {
 
-    @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var logger: UiEventLogger
+    @Mock private lateinit var accessibilityManager: AccessibilityManagerWrapper
 
     private lateinit var underTest: KeyguardLongPressInteractor
 
@@ -63,6 +63,14 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(accessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenAnswer {
+            it.arguments[0]
+        }
+
+        testScope = TestScope()
+        keyguardRepository = FakeKeyguardRepository()
+        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+
         runBlocking { createUnderTest() }
     }
 
@@ -98,60 +106,117 @@
         }
 
     @Test
-    fun `long-pressed - pop-up clicked - starts activity`() =
+    fun longPressed_menuClicked_showsSettings() =
         testScope.runTest {
-            val menu = collectLastValue(underTest.menu)
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
+            val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
             runCurrent()
 
-            val x = 100
-            val y = 123
-            underTest.onLongPress(x, y)
-            assertThat(menu()).isNotNull()
-            assertThat(menu()?.position?.x).isEqualTo(x)
-            assertThat(menu()?.position?.y).isEqualTo(y)
+            underTest.onLongPress()
+            assertThat(isMenuVisible).isTrue()
 
-            menu()?.onClicked?.invoke()
+            underTest.onMenuTouchGestureEnded(/* isClick= */ true)
 
-            assertThat(menu()).isNull()
-            verify(activityStarter).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+            assertThat(isMenuVisible).isFalse()
+            assertThat(shouldOpenSettings).isTrue()
         }
 
     @Test
-    fun `long-pressed - pop-up dismissed - never starts activity`() =
+    fun onSettingsShown_consumesSettingsShowEvent() =
         testScope.runTest {
-            val menu = collectLastValue(underTest.menu)
+            val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
             runCurrent()
 
-            menu()?.onDismissed?.invoke()
+            underTest.onLongPress()
+            underTest.onMenuTouchGestureEnded(/* isClick= */ true)
+            assertThat(shouldOpenSettings).isTrue()
 
-            assertThat(menu()).isNull()
-            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+            underTest.onSettingsShown()
+            assertThat(shouldOpenSettings).isFalse()
         }
 
-    @Suppress("DEPRECATION") // We're okay using ACTION_CLOSE_SYSTEM_DIALOGS on system UI.
+    @Test
+    fun onTouchedOutside_neverShowsSettings() =
+        testScope.runTest {
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
+            val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
+            runCurrent()
+
+            underTest.onTouchedOutside()
+
+            assertThat(isMenuVisible).isFalse()
+            assertThat(shouldOpenSettings).isFalse()
+        }
+
+    @Test
+    fun longPressed_openWppDirectlyEnabled_doesNotShowMenu_opensSettings() =
+        testScope.runTest {
+            createUnderTest(isOpenWppDirectlyEnabled = true)
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
+            val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
+            runCurrent()
+
+            underTest.onLongPress()
+
+            assertThat(isMenuVisible).isFalse()
+            assertThat(shouldOpenSettings).isTrue()
+        }
+
     @Test
     fun `long pressed - close dialogs broadcast received - popup dismissed`() =
         testScope.runTest {
-            val menu = collectLastValue(underTest.menu)
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
             runCurrent()
 
-            underTest.onLongPress(123, 456)
-            assertThat(menu()).isNotNull()
+            underTest.onLongPress()
+            assertThat(isMenuVisible).isTrue()
 
             fakeBroadcastDispatcher.registeredReceivers.forEach { broadcastReceiver ->
                 broadcastReceiver.onReceive(context, Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
             }
 
-            assertThat(menu()).isNull()
+            assertThat(isMenuVisible).isFalse()
+        }
+
+    @Test
+    fun closesDialogAfterTimeout() =
+        testScope.runTest {
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
+            runCurrent()
+
+            underTest.onLongPress()
+            assertThat(isMenuVisible).isTrue()
+
+            advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
+
+            assertThat(isMenuVisible).isFalse()
+        }
+
+    @Test
+    fun closesDialogAfterTimeout_onlyAfterTouchGestureEnded() =
+        testScope.runTest {
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
+            runCurrent()
+
+            underTest.onLongPress()
+            assertThat(isMenuVisible).isTrue()
+            underTest.onMenuTouchGestureStarted()
+
+            advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
+            assertThat(isMenuVisible).isTrue()
+
+            underTest.onMenuTouchGestureEnded(/* isClick= */ false)
+            advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
+            assertThat(isMenuVisible).isFalse()
         }
 
     @Test
     fun `logs when menu is shown`() =
         testScope.runTest {
-            collectLastValue(underTest.menu)
+            collectLastValue(underTest.isMenuVisible)
             runCurrent()
 
-            underTest.onLongPress(100, 123)
+            underTest.onLongPress()
 
             verify(logger)
                 .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
@@ -160,41 +225,61 @@
     @Test
     fun `logs when menu is clicked`() =
         testScope.runTest {
-            val menu = collectLastValue(underTest.menu)
+            collectLastValue(underTest.isMenuVisible)
             runCurrent()
 
-            underTest.onLongPress(100, 123)
-            menu()?.onClicked?.invoke()
+            underTest.onLongPress()
+            underTest.onMenuTouchGestureEnded(/* isClick= */ true)
 
             verify(logger)
                 .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
         }
 
+    @Test
+    fun showMenu_leaveLockscreen_returnToLockscreen_menuNotVisible() =
+        testScope.runTest {
+            val isMenuVisible by collectLastValue(underTest.isMenuVisible)
+            runCurrent()
+            underTest.onLongPress()
+            assertThat(isMenuVisible).isTrue()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    to = KeyguardState.GONE,
+                ),
+            )
+            assertThat(isMenuVisible).isFalse()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    to = KeyguardState.LOCKSCREEN,
+                ),
+            )
+            assertThat(isMenuVisible).isFalse()
+        }
+
     private suspend fun createUnderTest(
         isLongPressFeatureEnabled: Boolean = true,
         isRevampedWppFeatureEnabled: Boolean = true,
+        isOpenWppDirectlyEnabled: Boolean = false,
     ) {
-        testScope = TestScope()
-        keyguardRepository = FakeKeyguardRepository()
-        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
-
         underTest =
             KeyguardLongPressInteractor(
-                unsafeContext = context,
                 scope = testScope.backgroundScope,
                 transitionInteractor =
                     KeyguardTransitionInteractor(
                         repository = keyguardTransitionRepository,
                     ),
                 repository = keyguardRepository,
-                activityStarter = activityStarter,
                 logger = logger,
                 featureFlags =
                     FakeFeatureFlags().apply {
                         set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
                         set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled)
+                        set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, isOpenWppDirectlyEnabled)
                     },
                 broadcastDispatcher = fakeBroadcastDispatcher,
+                accessibilityManager = accessibilityManager
             )
         setUpState()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index bfc09d7..224eec1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -20,10 +20,12 @@
 import android.content.Intent
 import android.os.UserHandle
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
@@ -38,10 +40,13 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -51,6 +56,7 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.any
@@ -91,6 +97,8 @@
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var accessibilityManager: AccessibilityManagerWrapper
 
     private lateinit var underTest: KeyguardBottomAreaViewModel
 
@@ -134,6 +142,8 @@
             FakeFeatureFlags().apply {
                 set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
                 set(Flags.FACE_AUTH_REFACTOR, true)
+                set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
+                set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false)
             }
 
         val keyguardInteractor =
@@ -196,6 +206,19 @@
                 dumpManager = mock(),
                 userHandle = UserHandle.SYSTEM,
             )
+        val keyguardLongPressInteractor =
+            KeyguardLongPressInteractor(
+                scope = testScope.backgroundScope,
+                transitionInteractor =
+                    KeyguardTransitionInteractor(
+                        repository = FakeKeyguardTransitionRepository(),
+                    ),
+                repository = repository,
+                logger = UiEventLoggerFake(),
+                featureFlags = featureFlags,
+                broadcastDispatcher = broadcastDispatcher,
+                accessibilityManager = accessibilityManager,
+            )
         underTest =
             KeyguardBottomAreaViewModel(
                 keyguardInteractor = keyguardInteractor,
@@ -216,6 +239,14 @@
                     ),
                 bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
                 burnInHelperWrapper = burnInHelperWrapper,
+                longPressViewModel =
+                    KeyguardLongPressViewModel(
+                        interactor = keyguardLongPressInteractor,
+                    ),
+                settingsMenuViewModel =
+                    KeyguardSettingsMenuViewModel(
+                        interactor = keyguardLongPressInteractor,
+                    ),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 543875d..aace566 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -101,6 +101,7 @@
 import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -2199,6 +2200,7 @@
     }
 
     @Test
+    @Ignore("b/276920368")
     fun bindRecommendation_carouselNotFitThreeRecs() {
         useRealConstraintSets()
         setupUpdatedRecommendationViewHolder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 7b37ea0..9997997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -106,6 +106,7 @@
 import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSFragment;
@@ -295,6 +296,7 @@
     @Captor
     protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
             mEmptySpaceClickListenerCaptor;
+    @Mock protected ActivityStarter mActivityStarter;
 
     protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
     protected KeyguardInteractor mKeyguardInteractor;
@@ -575,7 +577,8 @@
                 () -> mMultiShadeInteractor,
                 mDumpManager,
                 mKeyuardLongPressViewModel,
-                mKeyguardInteractor);
+                mKeyguardInteractor,
+                mActivityStarter);
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
                 null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index d530829..b043e97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -42,11 +42,11 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.qs.ChipVisibilityListener
 import com.android.systemui.qs.HeaderPrivacyIconsController
-import com.android.systemui.qs.carrier.QSCarrierGroup
-import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
 import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
 import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
+import com.android.systemui.shade.carrier.ShadeCarrierGroup
+import com.android.systemui.shade.carrier.ShadeCarrierGroupController
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusIconContainer
@@ -88,11 +88,12 @@
     @Mock private lateinit var statusBarIconController: StatusBarIconController
     @Mock private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
     @Mock private lateinit var iconManager: StatusBarIconController.TintedIconManager
-    @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
-    @Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
+    @Mock private lateinit var mShadeCarrierGroupController: ShadeCarrierGroupController
+    @Mock
+    private lateinit var mShadeCarrierGroupControllerBuilder: ShadeCarrierGroupController.Builder
     @Mock private lateinit var clock: Clock
     @Mock private lateinit var date: VariableDateView
-    @Mock private lateinit var carrierGroup: QSCarrierGroup
+    @Mock private lateinit var carrierGroup: ShadeCarrierGroup
     @Mock private lateinit var batteryMeterView: BatteryMeterView
     @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
     @Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
@@ -131,7 +132,7 @@
         whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
         whenever(date.context).thenReturn(mockedContext)
 
-        whenever<QSCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup)
+        whenever<ShadeCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup)
 
         whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
             .thenReturn(batteryMeterView)
@@ -142,9 +143,10 @@
         whenever(view.context).thenReturn(viewContext)
         whenever(view.resources).thenReturn(context.resources)
         whenever(statusIcons.context).thenReturn(context)
-        whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
-            .thenReturn(qsCarrierGroupControllerBuilder)
-        whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+        whenever(mShadeCarrierGroupControllerBuilder.setShadeCarrierGroup(any()))
+            .thenReturn(mShadeCarrierGroupControllerBuilder)
+        whenever(mShadeCarrierGroupControllerBuilder.build())
+            .thenReturn(mShadeCarrierGroupController)
         whenever(view.setVisibility(anyInt())).then {
             viewVisibility = it.arguments[0] as Int
             null
@@ -175,7 +177,7 @@
                 variableDateViewControllerFactory,
                 batteryMeterViewController,
                 dumpManager,
-                qsCarrierGroupControllerBuilder,
+                mShadeCarrierGroupControllerBuilder,
                 combinedShadeHeadersConstraintManager,
                 demoModeController,
                 qsBatteryModeController,
@@ -189,7 +191,7 @@
     @Test
     fun updateListeners_registersWhenVisible() {
         makeShadeVisible()
-        verify(qsCarrierGroupController).setListening(true)
+        verify(mShadeCarrierGroupController).setListening(true)
         verify(statusBarIconController).addIconGroup(any())
     }
 
@@ -213,7 +215,7 @@
 
     @Test
     fun singleCarrier_enablesCarrierIconsInStatusIcons() {
-        whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+        whenever(mShadeCarrierGroupController.isSingleCarrier).thenReturn(true)
 
         makeShadeVisible()
 
@@ -222,7 +224,7 @@
 
     @Test
     fun dualCarrier_disablesCarrierIconsInStatusIcons() {
-        whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+        whenever(mShadeCarrierGroupController.isSingleCarrier).thenReturn(false)
 
         makeShadeVisible()
 
@@ -349,9 +351,9 @@
         verify(batteryMeterViewController).init()
         verify(batteryMeterViewController).ignoreTunerUpdates()
 
-        val inOrder = Mockito.inOrder(qsCarrierGroupControllerBuilder)
-        inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup)
-        inOrder.verify(qsCarrierGroupControllerBuilder).build()
+        val inOrder = Mockito.inOrder(mShadeCarrierGroupControllerBuilder)
+        inOrder.verify(mShadeCarrierGroupControllerBuilder).setShadeCarrierGroup(carrierGroup)
+        inOrder.verify(mShadeCarrierGroupControllerBuilder).build()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
index 75be74b..7a9ef62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier
+package com.android.systemui.shade.carrier
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -45,4 +45,4 @@
 
         assertNotSame(c, other)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
similarity index 84%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 1e7722a..2ef3d60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier;
+package com.android.systemui.shade.carrier;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -63,13 +63,13 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
-public class QSCarrierGroupControllerTest extends LeakCheckedTest {
+public class ShadeCarrierGroupControllerTest extends LeakCheckedTest {
 
-    private QSCarrierGroupController mQSCarrierGroupController;
+    private ShadeCarrierGroupController mShadeCarrierGroupController;
     private SignalCallback mSignalCallback;
     private CarrierTextManager.CarrierTextCallback mCallback;
     @Mock
-    private QSCarrierGroup mQSCarrierGroup;
+    private ShadeCarrierGroup mShadeCarrierGroup;
     @Mock
     private ActivityStarter mActivityStarter;
     @Mock
@@ -81,14 +81,14 @@
     @Mock
     private CarrierConfigTracker mCarrierConfigTracker;
     @Mock
-    private QSCarrier mQSCarrier1;
+    private ShadeCarrier mShadeCarrier1;
     @Mock
-    private QSCarrier mQSCarrier2;
+    private ShadeCarrier mShadeCarrier2;
     @Mock
-    private QSCarrier mQSCarrier3;
+    private ShadeCarrier mShadeCarrier3;
     private TestableLooper mTestableLooper;
     @Mock
-    private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
+    private ShadeCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
 
     private FakeSlotIndexResolver mSlotIndexResolver;
     private ClickListenerTextView mNoCarrierTextView;
@@ -116,28 +116,28 @@
                 .setListening(any(CarrierTextManager.CarrierTextCallback.class));
 
         mNoCarrierTextView = new ClickListenerTextView(mContext);
-        when(mQSCarrierGroup.getNoSimTextView()).thenReturn(mNoCarrierTextView);
-        when(mQSCarrierGroup.getCarrier1View()).thenReturn(mQSCarrier1);
-        when(mQSCarrierGroup.getCarrier2View()).thenReturn(mQSCarrier2);
-        when(mQSCarrierGroup.getCarrier3View()).thenReturn(mQSCarrier3);
-        when(mQSCarrierGroup.getCarrierDivider1()).thenReturn(new View(mContext));
-        when(mQSCarrierGroup.getCarrierDivider2()).thenReturn(new View(mContext));
+        when(mShadeCarrierGroup.getNoSimTextView()).thenReturn(mNoCarrierTextView);
+        when(mShadeCarrierGroup.getCarrier1View()).thenReturn(mShadeCarrier1);
+        when(mShadeCarrierGroup.getCarrier2View()).thenReturn(mShadeCarrier2);
+        when(mShadeCarrierGroup.getCarrier3View()).thenReturn(mShadeCarrier3);
+        when(mShadeCarrierGroup.getCarrierDivider1()).thenReturn(new View(mContext));
+        when(mShadeCarrierGroup.getCarrierDivider2()).thenReturn(new View(mContext));
 
         mSlotIndexResolver = new FakeSlotIndexResolver();
 
-        mQSCarrierGroupController = new QSCarrierGroupController.Builder(
+        mShadeCarrierGroupController = new ShadeCarrierGroupController.Builder(
                 mActivityStarter, handler, TestableLooper.get(this).getLooper(),
                 mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
                 mSlotIndexResolver)
-                .setQSCarrierGroup(mQSCarrierGroup)
+                .setShadeCarrierGroup(mShadeCarrierGroup)
                 .build();
 
-        mQSCarrierGroupController.setListening(true);
+        mShadeCarrierGroupController.setListening(true);
     }
 
     @Test
     public void testInitiallyMultiCarrier() {
-        assertFalse(mQSCarrierGroupController.isSingleCarrier());
+        assertFalse(mShadeCarrierGroupController.isSingleCarrier());
     }
 
     @Test // throws no Exception
@@ -257,12 +257,12 @@
                 true /* airplaneMode */);
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
-        assertEquals(View.GONE, mQSCarrierGroup.getNoSimTextView().getVisibility());
+        assertEquals(View.GONE, mShadeCarrierGroup.getNoSimTextView().getVisibility());
     }
 
     @Test
     public void testListenerNotCalledOnRegistreation() {
-        mQSCarrierGroupController
+        mShadeCarrierGroupController
                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
 
         verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
@@ -282,9 +282,9 @@
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
 
-        verify(mQSCarrier1).updateState(any(), eq(true));
-        verify(mQSCarrier2).updateState(any(), eq(true));
-        verify(mQSCarrier3).updateState(any(), eq(true));
+        verify(mShadeCarrier1).updateState(any(), eq(true));
+        verify(mShadeCarrier2).updateState(any(), eq(true));
+        verify(mShadeCarrier3).updateState(any(), eq(true));
     }
 
     @Test
@@ -301,9 +301,9 @@
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
 
-        verify(mQSCarrier1).updateState(any(), eq(false));
-        verify(mQSCarrier2).updateState(any(), eq(false));
-        verify(mQSCarrier3).updateState(any(), eq(false));
+        verify(mShadeCarrier1).updateState(any(), eq(false));
+        verify(mShadeCarrier2).updateState(any(), eq(false));
+        verify(mShadeCarrier3).updateState(any(), eq(false));
     }
 
     @Test
@@ -327,7 +327,7 @@
         mCallback.updateCarrierInfo(singleCarrierInfo);
         mTestableLooper.processAllMessages();
 
-        mQSCarrierGroupController
+        mShadeCarrierGroupController
                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
         reset(mOnSingleCarrierChangedListener);
 
@@ -353,7 +353,7 @@
         mCallback.updateCarrierInfo(singleCarrierInfo);
         mTestableLooper.processAllMessages();
 
-        mQSCarrierGroupController
+        mShadeCarrierGroupController
                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
 
         mCallback.updateCarrierInfo(singleCarrierInfo);
@@ -375,7 +375,7 @@
         mCallback.updateCarrierInfo(multiCarrierInfo);
         mTestableLooper.processAllMessages();
 
-        mQSCarrierGroupController
+        mShadeCarrierGroupController
                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
 
         mCallback.updateCarrierInfo(multiCarrierInfo);
@@ -389,12 +389,12 @@
         ArgumentCaptor<View.OnClickListener> captor =
                 ArgumentCaptor.forClass(View.OnClickListener.class);
 
-        verify(mQSCarrier1).setOnClickListener(captor.capture());
-        verify(mQSCarrier2).setOnClickListener(captor.getValue());
-        verify(mQSCarrier3).setOnClickListener(captor.getValue());
+        verify(mShadeCarrier1).setOnClickListener(captor.capture());
+        verify(mShadeCarrier2).setOnClickListener(captor.getValue());
+        verify(mShadeCarrier3).setOnClickListener(captor.getValue());
 
         assertThat(mNoCarrierTextView.getOnClickListener()).isSameInstanceAs(captor.getValue());
-        verify(mQSCarrierGroup, never()).setOnClickListener(any());
+        verify(mShadeCarrierGroup, never()).setOnClickListener(any());
     }
 
     @Test
@@ -402,10 +402,10 @@
         ArgumentCaptor<View.OnClickListener> captor =
                 ArgumentCaptor.forClass(View.OnClickListener.class);
 
-        verify(mQSCarrier1).setOnClickListener(captor.capture());
-        when(mQSCarrier1.isVisibleToUser()).thenReturn(false);
+        verify(mShadeCarrier1).setOnClickListener(captor.capture());
+        when(mShadeCarrier1.isVisibleToUser()).thenReturn(false);
 
-        captor.getValue().onClick(mQSCarrier1);
+        captor.getValue().onClick(mShadeCarrier1);
         verifyZeroInteractions(mActivityStarter);
     }
 
@@ -415,17 +415,17 @@
                 ArgumentCaptor.forClass(View.OnClickListener.class);
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
 
-        verify(mQSCarrier1).setOnClickListener(listenerCaptor.capture());
-        when(mQSCarrier1.isVisibleToUser()).thenReturn(true);
+        verify(mShadeCarrier1).setOnClickListener(listenerCaptor.capture());
+        when(mShadeCarrier1.isVisibleToUser()).thenReturn(true);
 
-        listenerCaptor.getValue().onClick(mQSCarrier1);
+        listenerCaptor.getValue().onClick(mShadeCarrier1);
         verify(mActivityStarter)
                 .postStartActivityDismissingKeyguard(intentCaptor.capture(), anyInt());
         assertThat(intentCaptor.getValue().getAction())
                 .isEqualTo(Settings.ACTION_WIRELESS_SETTINGS);
     }
 
-    private class FakeSlotIndexResolver implements QSCarrierGroupController.SlotIndexResolver {
+    private class FakeSlotIndexResolver implements ShadeCarrierGroupController.SlotIndexResolver {
         public boolean overrideInvalid;
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
similarity index 67%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
index 9115ab3..4461310 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.carrier;
+package com.android.systemui.shade.carrier;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -39,9 +39,9 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
-public class QSCarrierTest extends SysuiTestCase {
+public class ShadeCarrierTest extends SysuiTestCase {
 
-    private QSCarrier mQSCarrier;
+    private ShadeCarrier mShadeCarrier;
     private TestableLooper mTestableLooper;
     private int mSignalIconId;
 
@@ -51,7 +51,7 @@
         LayoutInflater inflater = LayoutInflater.from(mContext);
         mContext.ensureTestableResources();
         mTestableLooper.runWithLooper(() ->
-                mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+                mShadeCarrier = (ShadeCarrier) inflater.inflate(R.layout.shade_carrier, null));
 
         // In this case, the id is an actual drawable id
         mSignalIconId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
@@ -61,76 +61,76 @@
     public void testUpdateState_first() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        assertTrue(mQSCarrier.updateState(c, false));
+        assertTrue(mShadeCarrier.updateState(c, false));
     }
 
     @Test
     public void testUpdateState_same() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        assertTrue(mQSCarrier.updateState(c, false));
-        assertFalse(mQSCarrier.updateState(c, false));
+        assertTrue(mShadeCarrier.updateState(c, false));
+        assertFalse(mShadeCarrier.updateState(c, false));
     }
 
     @Test
     public void testUpdateState_changed() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        assertTrue(mQSCarrier.updateState(c, false));
+        assertTrue(mShadeCarrier.updateState(c, false));
 
         CellSignalState other = c.changeVisibility(false);
 
-        assertTrue(mQSCarrier.updateState(other, false));
+        assertTrue(mShadeCarrier.updateState(other, false));
     }
 
     @Test
     public void testUpdateState_singleCarrier_first() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        assertTrue(mQSCarrier.updateState(c, true));
+        assertTrue(mShadeCarrier.updateState(c, true));
     }
 
     @Test
     public void testUpdateState_singleCarrier_noShowIcon() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        mQSCarrier.updateState(c, true);
+        mShadeCarrier.updateState(c, true);
 
-        assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
+        assertEquals(View.GONE, mShadeCarrier.getRSSIView().getVisibility());
     }
 
     @Test
     public void testUpdateState_multiCarrier_showIcon() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        mQSCarrier.updateState(c, false);
+        mShadeCarrier.updateState(c, false);
 
-        assertEquals(View.VISIBLE, mQSCarrier.getRSSIView().getVisibility());
+        assertEquals(View.VISIBLE, mShadeCarrier.getRSSIView().getVisibility());
     }
 
     @Test
     public void testUpdateState_changeSingleMultiSingle() {
         CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
-        mQSCarrier.updateState(c, true);
-        assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
+        mShadeCarrier.updateState(c, true);
+        assertEquals(View.GONE, mShadeCarrier.getRSSIView().getVisibility());
 
-        mQSCarrier.updateState(c, false);
-        assertEquals(View.VISIBLE, mQSCarrier.getRSSIView().getVisibility());
+        mShadeCarrier.updateState(c, false);
+        assertEquals(View.VISIBLE, mShadeCarrier.getRSSIView().getVisibility());
 
-        mQSCarrier.updateState(c, true);
-        assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
+        mShadeCarrier.updateState(c, true);
+        assertEquals(View.GONE, mShadeCarrier.getRSSIView().getVisibility());
     }
 
     @Test
     public void testCarrierNameMaxWidth_smallScreen_fromResource() {
         int maxEms = 10;
-        mContext.getOrCreateTestableResources().addOverride(R.integer.qs_carrier_max_em, maxEms);
+        mContext.getOrCreateTestableResources().addOverride(R.integer.shade_carrier_max_em, maxEms);
         mContext.getOrCreateTestableResources()
                 .addOverride(R.bool.config_use_large_screen_shade_header, false);
-        TextView carrierText = mQSCarrier.requireViewById(R.id.qs_carrier_text);
+        TextView carrierText = mShadeCarrier.requireViewById(R.id.shade_carrier_text);
 
-        mQSCarrier.onConfigurationChanged(mContext.getResources().getConfiguration());
+        mShadeCarrier.onConfigurationChanged(mContext.getResources().getConfiguration());
 
         assertEquals(maxEms, carrierText.getMaxEms());
     }
@@ -138,12 +138,12 @@
     @Test
     public void testCarrierNameMaxWidth_largeScreen_maxInt() {
         int maxEms = 10;
-        mContext.getOrCreateTestableResources().addOverride(R.integer.qs_carrier_max_em, maxEms);
+        mContext.getOrCreateTestableResources().addOverride(R.integer.shade_carrier_max_em, maxEms);
         mContext.getOrCreateTestableResources()
                 .addOverride(R.bool.config_use_large_screen_shade_header, true);
-        TextView carrierText = mQSCarrier.requireViewById(R.id.qs_carrier_text);
+        TextView carrierText = mShadeCarrier.requireViewById(R.id.shade_carrier_text);
 
-        mQSCarrier.onConfigurationChanged(mContext.getResources().getConfiguration());
+        mShadeCarrier.onConfigurationChanged(mContext.getResources().getConfiguration());
 
         assertEquals(Integer.MAX_VALUE, carrierText.getMaxEms());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 30708a7..ac66ad9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -97,6 +97,59 @@
     }
 
     @Test
+    public void highImportanceConversation() {
+        // GIVEN notification is high importance and is a people notification
+        final Notification notification = new Notification.Builder(mContext, "test")
+                .build();
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_HIGH)
+                .build();
+        when(mPeopleNotificationIdentifier
+                .getPeopleNotificationType(entry))
+                .thenReturn(TYPE_PERSON);
+
+        // THEN it is high priority conversation
+        assertTrue(mHighPriorityProvider.isHighPriorityConversation(entry));
+    }
+
+    @Test
+    public void lowImportanceConversation() {
+        // GIVEN notification is high importance and is a people notification
+        final Notification notification = new Notification.Builder(mContext, "test")
+                .build();
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_LOW)
+                .build();
+        when(mPeopleNotificationIdentifier
+                .getPeopleNotificationType(entry))
+                .thenReturn(TYPE_PERSON);
+
+        // THEN it is low priority conversation
+        assertFalse(mHighPriorityProvider.isHighPriorityConversation(entry));
+    }
+
+    @Test
+    public void highImportanceConversationWhenAnyOfChildIsHighPriority() {
+        // GIVEN notification is high importance and is a people notification
+        final NotificationEntry summary = createNotifEntry(false);
+        final NotificationEntry lowPriorityChild = createNotifEntry(false);
+        final NotificationEntry highPriorityChild = createNotifEntry(true);
+        when(mPeopleNotificationIdentifier
+                .getPeopleNotificationType(summary))
+                .thenReturn(TYPE_PERSON);
+        final GroupEntry groupEntry = new GroupEntryBuilder()
+                .setParent(GroupEntry.ROOT_ENTRY)
+                .setSummary(summary)
+                .setChildren(List.of(lowPriorityChild, highPriorityChild))
+                .build();
+
+        // THEN the groupEntry is high priority conversation since it has a high priority child
+        assertTrue(mHighPriorityProvider.isHighPriorityConversation(groupEntry));
+    }
+
+    @Test
     public void messagingStyle() {
         // GIVEN notification is low importance but has messaging style
         final Notification notification = new Notification.Builder(mContext, "test")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 742fcf5..55ea3157 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.statusbar.notification.collection.coordinator
 
 import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.app.NotificationManager.IMPORTANCE_LOW
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
@@ -31,10 +34,13 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.icon.ConversationIconManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.withArgCaptor
@@ -55,7 +61,8 @@
 class ConversationCoordinatorTest : SysuiTestCase() {
     // captured listeners and pluggables:
     private lateinit var promoter: NotifPromoter
-    private lateinit var peopleSectioner: NotifSectioner
+    private lateinit var peopleAlertingSectioner: NotifSectioner
+    private lateinit var peopleSilentSectioner: NotifSectioner
     private lateinit var peopleComparator: NotifComparator
     private lateinit var beforeRenderListListener: OnBeforeRenderListListener
 
@@ -76,6 +83,7 @@
         coordinator = ConversationCoordinator(
             peopleNotificationIdentifier,
             conversationIconManager,
+            HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()),
             headerController
         )
         whenever(channel.isImportantConversation).thenReturn(true)
@@ -90,12 +98,13 @@
             verify(pipeline).addOnBeforeRenderListListener(capture())
         }
 
-        peopleSectioner = coordinator.sectioner
-        peopleComparator = peopleSectioner.comparator!!
+        peopleAlertingSectioner = coordinator.peopleAlertingSectioner
+        peopleSilentSectioner = coordinator.peopleSilentSectioner
+        peopleComparator = peopleAlertingSectioner.comparator!!
 
         entry = NotificationEntryBuilder().setChannel(channel).build()
 
-        val section = NotifSection(peopleSectioner, 0)
+        val section = NotifSection(peopleAlertingSectioner, 0)
         entryA = NotificationEntryBuilder().setChannel(channel)
             .setSection(section).setTag("A").build()
         entryB = NotificationEntryBuilder().setChannel(channel)
@@ -129,13 +138,67 @@
     }
 
     @Test
-    fun testInPeopleSection() {
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry))
-            .thenReturn(TYPE_PERSON)
+    fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
+        // GIVEN
+        val alertingEntry = NotificationEntryBuilder().setChannel(channel)
+                .setImportance(IMPORTANCE_DEFAULT).build()
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry))
+                .thenReturn(TYPE_PERSON)
 
-        // only put people notifications in this section
-        assertTrue(peopleSectioner.isInSection(entry))
-        assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build()))
+        // put alerting people notifications in this section
+        assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue()
+       }
+
+    @Test
+    fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
+        // GIVEN
+        val silentEntry = NotificationEntryBuilder().setChannel(channel)
+                .setImportance(IMPORTANCE_LOW).build()
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry))
+                .thenReturn(TYPE_PERSON)
+
+        // THEN put silent people notifications in this section
+        assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue()
+        // People Alerting sectioning happens before the silent one.
+        // It claims high important conversations and rest of conversations will be considered as silent.
+        assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isFalse()
+    }
+
+    @Test
+    fun testNotInPeopleSection() {
+        // GIVEN
+        val entry = NotificationEntryBuilder().setChannel(channel)
+                .setImportance(IMPORTANCE_LOW).build()
+        val importantEntry = NotificationEntryBuilder().setChannel(channel)
+                .setImportance(IMPORTANCE_HIGH).build()
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry))
+                .thenReturn(TYPE_NON_PERSON)
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry))
+                .thenReturn(TYPE_NON_PERSON)
+
+        // THEN - only put people notification either silent or alerting
+        assertThat(peopleSilentSectioner.isInSection(entry)).isFalse()
+        assertThat(peopleAlertingSectioner.isInSection(importantEntry)).isFalse()
+    }
+
+    @Test
+    fun testInAlertingPeopleSectionWhenThereIsAnImportantChild(){
+        // GIVEN
+        val altChildA = NotificationEntryBuilder().setTag("A")
+                .setImportance(IMPORTANCE_DEFAULT).build()
+        val altChildB = NotificationEntryBuilder().setTag("B")
+                .setImportance(IMPORTANCE_LOW).build()
+        val summary = NotificationEntryBuilder().setId(2)
+                .setImportance(IMPORTANCE_LOW).setChannel(channel).build()
+        val groupEntry = GroupEntryBuilder()
+                .setParent(GroupEntry.ROOT_ENTRY)
+                .setSummary(summary)
+                .setChildren(listOf(altChildA, altChildB))
+                .build()
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary))
+                .thenReturn(TYPE_PERSON)
+        // THEN
+        assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index d5c0c55..3d1253e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -52,7 +52,6 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 
@@ -73,7 +72,6 @@
 
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private HighPriorityProvider mHighPriorityProvider;
-    @Mock private SectionStyleProvider mSectionStyleProvider;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private NodeController mAlertingHeaderController;
     @Mock private NodeController mSilentNodeController;
@@ -100,7 +98,6 @@
         mRankingCoordinator = new RankingCoordinator(
                 mStatusBarStateController,
                 mHighPriorityProvider,
-                mSectionStyleProvider,
                 mAlertingHeaderController,
                 mSilentHeaderController,
                 mSilentNodeController);
@@ -108,7 +105,6 @@
         mEntry.setRanking(getRankingForUnfilteredNotif().build());
 
         mRankingCoordinator.attach(mNotifPipeline);
-        verify(mSectionStyleProvider).setMinimizedSections(any());
         verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
         mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
         mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
new file mode 100644
index 0000000..cbb0894
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -0,0 +1,78 @@
+package com.android.systemui.statusbar.notification.interruption
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.NO_FSI_NOT_IMPORTANT_ENOUGH
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotificationInterruptStateProviderWrapperTest : SysuiTestCase() {
+
+    @Test
+    fun decisionOfTrue() {
+        assertTrue(DecisionImpl.of(true).shouldInterrupt)
+    }
+
+    @Test
+    fun decisionOfFalse() {
+        assertFalse(DecisionImpl.of(false).shouldInterrupt)
+    }
+
+    @Test
+    fun decisionOfTrueInterned() {
+        assertEquals(DecisionImpl.of(true), DecisionImpl.of(true))
+    }
+
+    @Test
+    fun decisionOfFalseInterned() {
+        assertEquals(DecisionImpl.of(false), DecisionImpl.of(false))
+    }
+
+    @Test
+    fun fullScreenIntentDecisionShouldInterrupt() {
+        makeFsiDecision(FSI_DEVICE_NOT_INTERACTIVE).let {
+            assertTrue(it.shouldInterrupt)
+            assertFalse(it.wouldInterruptWithoutDnd)
+        }
+    }
+
+    @Test
+    fun fullScreenIntentDecisionShouldNotInterrupt() {
+        makeFsiDecision(NO_FSI_NOT_IMPORTANT_ENOUGH).let {
+            assertFalse(it.shouldInterrupt)
+            assertFalse(it.wouldInterruptWithoutDnd)
+        }
+    }
+
+    @Test
+    fun fullScreenIntentDecisionWouldInterruptWithoutDnd() {
+        makeFsiDecision(NO_FSI_SUPPRESSED_ONLY_BY_DND).let {
+            assertFalse(it.shouldInterrupt)
+            assertTrue(it.wouldInterruptWithoutDnd)
+        }
+    }
+
+    @Test
+    fun fullScreenIntentDecisionWouldNotInterruptEvenWithoutDnd() {
+        makeFsiDecision(NO_FSI_SUPPRESSED_BY_DND).let {
+            assertFalse(it.shouldInterrupt)
+            assertFalse(it.wouldInterruptWithoutDnd)
+        }
+    }
+
+    private fun makeFsiDecision(originalDecision: FullScreenIntentDecision) =
+        FullScreenIntentDecisionImpl(NotificationEntryBuilder().build(), originalDecision)
+}
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 1b6ab4d..297cb9d 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
@@ -179,15 +179,71 @@
         }
 
     @Test
-    fun iconId_cutout_whenDefaultDataDisabled() =
+    fun icon_usesLevelFromInteractor() =
+        testScope.runTest {
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
+
+            interactor.level.value = 3
+            assertThat(latest!!.level).isEqualTo(3)
+
+            interactor.level.value = 1
+            assertThat(latest!!.level).isEqualTo(1)
+
+            job.cancel()
+        }
+
+    @Test
+    fun icon_usesNumberOfLevelsFromInteractor() =
+        testScope.runTest {
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
+
+            interactor.numberOfLevels.value = 5
+            assertThat(latest!!.numberOfLevels).isEqualTo(5)
+
+            interactor.numberOfLevels.value = 2
+            assertThat(latest!!.numberOfLevels).isEqualTo(2)
+
+            job.cancel()
+        }
+
+    @Test
+    fun icon_defaultDataDisabled_showExclamationTrue() =
         testScope.runTest {
             interactor.setIsDefaultDataEnabled(false)
 
             var latest: SignalIconModel? = null
             val job = underTest.icon.onEach { latest = it }.launchIn(this)
-            val expected = defaultSignal(level = 1, connected = false)
 
-            assertThat(latest).isEqualTo(expected)
+            assertThat(latest!!.showExclamationMark).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun icon_defaultConnectionFailed_showExclamationTrue() =
+        testScope.runTest {
+            interactor.isDefaultConnectionFailed.value = true
+
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest!!.showExclamationMark).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun icon_enabledAndNotFailed_showExclamationFalse() =
+        testScope.runTest {
+            interactor.setIsDefaultDataEnabled(true)
+            interactor.isDefaultConnectionFailed.value = false
+
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest!!.showExclamationMark).isFalse()
 
             job.cancel()
         }
@@ -572,16 +628,14 @@
 
     companion object {
         private const val SUB_1_ID = 1
+        private const val NUM_LEVELS = 4
 
         /** Convenience constructor for these tests */
-        fun defaultSignal(
-            level: Int = 1,
-            connected: Boolean = true,
-        ): SignalIconModel {
-            return SignalIconModel(level, numberOfLevels = 4, showExclamationMark = !connected)
+        fun defaultSignal(level: Int = 1): SignalIconModel {
+            return SignalIconModel(level, NUM_LEVELS, showExclamationMark = false)
         }
 
         fun emptySignal(): SignalIconModel =
-            SignalIconModel(level = 0, numberOfLevels = 4, showExclamationMark = true)
+            SignalIconModel(level = 0, numberOfLevels = NUM_LEVELS, showExclamationMark = true)
     }
 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 78cbf2b..7b618b1 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -88,6 +88,7 @@
 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED;
 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER;
 import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT;
+import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
@@ -3240,6 +3241,12 @@
                 return;
             }
             Slog.e(TAG_SERVICE, "Short FGS timed out: " + sr);
+            final long now = SystemClock.uptimeMillis();
+            logFGSStateChangeLocked(sr,
+                    FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
+                    now > sr.mFgsEnterTime ? (int) (now - sr.mFgsEnterTime) : 0,
+                    FGS_STOP_REASON_UNKNOWN,
+                    FGS_TYPE_POLICY_CHECK_UNKNOWN);
             try {
                 sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId());
             } catch (RemoteException e) {
@@ -7897,7 +7904,8 @@
         boolean allowWhileInUsePermissionInFgs;
         @PowerExemptionManager.ReasonCode int fgsStartReasonCode;
         if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER
-                || state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) {
+                || state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT
+                || state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT) {
             allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering;
             fgsStartReasonCode = r.mAllowStartForegroundAtEntering;
         } else {
@@ -7931,9 +7939,9 @@
                 r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mClientUid : INVALID_UID,
                 r.mFgsDelegation != null ? r.mFgsDelegation.mOptions.mDelegationService
                         : ForegroundServiceDelegationOptions.DELEGATION_SERVICE_DEFAULT,
-                0,
-                null,
-                null);
+                0 /* api_sate */,
+                null /* api_type */,
+                null /* api_timestamp */);
 
         int event = 0;
         if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
@@ -7942,7 +7950,9 @@
             event = EventLogTags.AM_FOREGROUND_SERVICE_STOP;
         } else if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED) {
             event = EventLogTags.AM_FOREGROUND_SERVICE_DENIED;
-        } else {
+        } else if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT) {
+            event = EventLogTags.AM_FOREGROUND_SERVICE_TIMED_OUT;
+        }else {
             // Unknown event.
             return;
         }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index dbb351b..bfc8251 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -441,17 +441,17 @@
     }
 
     public int getPreferredSchedulingGroupLocked() {
-        if (mCountForeground > mCountForegroundDeferred) {
+        if (!isActive()) {
+            return ProcessList.SCHED_GROUP_UNDEFINED;
+        } else if (mCountForeground > mCountForegroundDeferred) {
             // We have a foreground broadcast somewhere down the queue, so
             // boost priority until we drain them all
             return ProcessList.SCHED_GROUP_DEFAULT;
         } else if ((mActive != null) && mActive.isForeground()) {
             // We have a foreground broadcast right now, so boost priority
             return ProcessList.SCHED_GROUP_DEFAULT;
-        } else if (!isIdle()) {
-            return ProcessList.SCHED_GROUP_BACKGROUND;
         } else {
-            return ProcessList.SCHED_GROUP_UNDEFINED;
+            return ProcessList.SCHED_GROUP_BACKGROUND;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 78edbba..568997b 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -178,6 +178,7 @@
     private static final String ATRACE_FREEZER_TRACK = "Freezer";
 
     private static final int FREEZE_BINDER_TIMEOUT_MS = 100;
+    private static final int FREEZE_DEADLOCK_TIMEOUT_MS = 1000;
 
     @VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false;
 
@@ -244,6 +245,7 @@
     static final int REPORT_UNFREEZE_MSG = 4;
     static final int COMPACT_NATIVE_MSG = 5;
     static final int UID_FROZEN_STATE_CHANGED_MSG = 6;
+    static final int DEADLOCK_WATCHDOG_MSG = 7;
 
     // When free swap falls below this percentage threshold any full (file + anon)
     // compactions will be downgraded to file only compactions to reduce pressure
@@ -1947,29 +1949,15 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case SET_FROZEN_PROCESS_MSG:
-                {
                     ProcessRecord proc = (ProcessRecord) msg.obj;
-                    int pid = proc.getPid();
-                    final String name = proc.processName;
                     synchronized (mAm) {
                         freezeProcess(proc);
                     }
-                    try {
-                        // post-check to prevent deadlock
-                        mProcLocksReader.handleBlockingFileLocks(this);
-                    } catch (Exception e) {
-                        Slog.e(TAG_AM, "Unable to check file locks for "
-                                + name + "(" + pid + "): " + e);
-                        synchronized (mAm) {
-                            synchronized (mProcLock) {
-                                unfreezeAppLSP(proc, UNFREEZE_REASON_FILE_LOCK_CHECK_FAILURE);
-                            }
-                        }
-                    }
                     if (proc.mOptRecord.isFrozen()) {
                         onProcessFrozen(proc);
+                        removeMessages(DEADLOCK_WATCHDOG_MSG);
+                        sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);
                     }
-                }
                     break;
                 case REPORT_UNFREEZE_MSG:
                     int pid = msg.arg1;
@@ -1981,8 +1969,18 @@
                     reportUnfreeze(pid, frozenDuration, processName, reason);
                     break;
                 case UID_FROZEN_STATE_CHANGED_MSG:
-                    ProcessRecord proc = (ProcessRecord) msg.obj;
-                    reportOneUidFrozenStateChanged(proc.uid, true);
+                    reportOneUidFrozenStateChanged(((ProcessRecord) msg.obj).uid, true);
+                    break;
+                case DEADLOCK_WATCHDOG_MSG:
+                    try {
+                        // post-check to prevent deadlock
+                        if (DEBUG_FREEZER) {
+                            Slog.d(TAG_AM, "Freezer deadlock watchdog");
+                        }
+                        mProcLocksReader.handleBlockingFileLocks(this);
+                    } catch (IOException e) {
+                        Slog.w(TAG_AM, "Unable to check file locks");
+                    }
                     break;
                 default:
                     return;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 81b24215..9e9db6a 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -121,10 +121,11 @@
 # Similarly, tags below are used by UserManagerService
 30091 um_user_visibility_changed (userId|1|5),(visible|1)
 
-# Foreground service start/stop events.
+# Foreground service start/stop/denied/timed_out events.
 30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
 30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
 30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
+30103 am_foreground_service_timed_out (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
 
 # Intent Sender redirect for UserHandle.USER_CURRENT
 30110 am_intent_sender_redirect_user (userId|1|5)
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 60a7f93..22e2c9f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -83,6 +83,7 @@
         DeviceConfig.NAMESPACE_CAMERA_NATIVE,
         DeviceConfig.NAMESPACE_CONFIGURATION,
         DeviceConfig.NAMESPACE_CONNECTIVITY,
+        DeviceConfig.NAMESPACE_EDGETPU_NATIVE,
         DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
         DeviceConfig.NAMESPACE_LMKD_NATIVE,
diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java
index 937e3f8..bac4480 100644
--- a/services/core/java/com/android/server/biometrics/BiometricSensor.java
+++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java
@@ -22,14 +22,20 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.SensorPropertiesInternal;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Wraps IBiometricAuthenticator implementation and stores information about the authenticator,
@@ -67,6 +73,7 @@
     public final int id;
     public final @Authenticators.Types int oemStrength; // strength as configured by the OEM
     public final int modality;
+    @NonNull public final List<ComponentInfoInternal> componentInfo;
     public final IBiometricAuthenticator impl;
 
     private @Authenticators.Types int mUpdatedStrength; // updated by BiometricStrengthController
@@ -86,15 +93,16 @@
      */
     abstract boolean confirmationSupported();
 
-    BiometricSensor(@NonNull Context context, int id, int modality,
-            @Authenticators.Types int strength, IBiometricAuthenticator impl) {
+    BiometricSensor(@NonNull Context context, int modality, @NonNull SensorPropertiesInternal props,
+            IBiometricAuthenticator impl) {
         this.mContext = context;
-        this.id = id;
+        this.id = props.sensorId;
         this.modality = modality;
-        this.oemStrength = strength;
+        this.oemStrength = Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
+        this.componentInfo = Collections.unmodifiableList(props.componentInfo);
         this.impl = impl;
 
-        mUpdatedStrength = strength;
+        mUpdatedStrength = oemStrength;
         goToStateUnknown();
     }
 
@@ -178,8 +186,25 @@
         return "ID(" + id + ")"
                 + ", oemStrength: " + oemStrength
                 + ", updatedStrength: " + mUpdatedStrength
-                + ", modality " + modality
+                + ", modality: " + modality
                 + ", state: " + mSensorState
                 + ", cookie: " + mCookie;
     }
+
+    protected void dump(@NonNull IndentingPrintWriter pw) {
+        pw.println(TextUtils.formatSimple("ID: %d", id));
+        pw.increaseIndent();
+        pw.println(TextUtils.formatSimple("oemStrength: %d", oemStrength));
+        pw.println(TextUtils.formatSimple("updatedStrength: %d", mUpdatedStrength));
+        pw.println(TextUtils.formatSimple("modality: %d", modality));
+        pw.println("componentInfo:");
+        for (ComponentInfoInternal info : componentInfo) {
+            pw.increaseIndent();
+            info.dump(pw);
+            pw.decreaseIndent();
+        }
+        pw.println(TextUtils.formatSimple("state: %d", mSensorState));
+        pw.println(TextUtils.formatSimple("cookie: %d", mCookie));
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index ffa5d20..f44d14b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -62,6 +62,7 @@
 import android.security.KeyStore;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -638,13 +639,16 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override
-        public synchronized void registerAuthenticator(int id, int modality,
-                @Authenticators.Types int strength,
+        public synchronized void registerAuthenticator(int modality,
+                @NonNull SensorPropertiesInternal props,
                 @NonNull IBiometricAuthenticator authenticator) {
 
             super.registerAuthenticator_enforcePermission();
 
-            Slog.d(TAG, "Registering ID: " + id
+            @Authenticators.Types final int strength =
+                    Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
+
+            Slog.d(TAG, "Registering ID: " + props.sensorId
                     + " Modality: " + modality
                     + " Strength: " + strength);
 
@@ -665,12 +669,12 @@
             }
 
             for (BiometricSensor sensor : mSensors) {
-                if (sensor.id == id) {
+                if (sensor.id == props.sensorId) {
                     throw new IllegalStateException("Cannot register duplicate authenticator");
                 }
             }
 
-            mSensors.add(new BiometricSensor(getContext(), id, modality, strength, authenticator) {
+            mSensors.add(new BiometricSensor(getContext(), modality, props, authenticator) {
                 @Override
                 boolean confirmationAlwaysRequired(int userId) {
                     return mSettingObserver.getConfirmationAlwaysRequired(modality, userId);
@@ -1360,13 +1364,17 @@
         return null;
     }
 
-    private void dumpInternal(PrintWriter pw) {
+    private void dumpInternal(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
+
         pw.println("Legacy Settings: " + mSettingObserver.mUseLegacyFaceOnlySettings);
         pw.println();
 
         pw.println("Sensors:");
         for (BiometricSensor sensor : mSensors) {
-            pw.println(" " + sensor);
+            pw.increaseIndent();
+            sensor.dump(pw);
+            pw.decreaseIndent();
         }
         pw.println();
         pw.println("CurrentSession: " + mAuthSession);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
index 0f0a81d..d43045b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
@@ -28,7 +27,6 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BiometricServiceRegistry;
 
 import java.util.List;
@@ -53,10 +51,8 @@
     @Override
     protected void registerService(@NonNull IBiometricService service,
             @NonNull FaceSensorPropertiesInternal props) {
-        @BiometricManager.Authenticators.Types final int strength =
-                Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
         try {
-            service.registerAuthenticator(props.sensorId, TYPE_FACE, strength,
+            service.registerAuthenticator(TYPE_FACE, props,
                     new FaceAuthenticator(mService, props.sensorId));
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
index 33810b7..6d210ea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
@@ -28,7 +27,6 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BiometricServiceRegistry;
 
 import java.util.List;
@@ -53,10 +51,8 @@
     @Override
     protected void registerService(@NonNull IBiometricService service,
             @NonNull FingerprintSensorPropertiesInternal props) {
-        @BiometricManager.Authenticators.Types final int strength =
-                Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
         try {
-            service.registerAuthenticator(props.sensorId, TYPE_FINGERPRINT, strength,
+            service.registerAuthenticator(TYPE_FINGERPRINT, props,
                     new FingerprintAuthenticator(mService, props.sensorId));
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
index 35ea36c..f27761f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
@@ -16,12 +16,10 @@
 
 package com.android.server.biometrics.sensors.iris;
 
-import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.hardware.iris.IIrisService;
@@ -33,7 +31,6 @@
 
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
-import com.android.server.biometrics.Utils;
 
 import java.util.List;
 
@@ -75,17 +72,12 @@
                         ServiceManager.getService(Context.BIOMETRIC_SERVICE));
 
                 for (SensorPropertiesInternal hidlSensor : hidlSensors) {
-                    final int sensorId = hidlSensor.sensorId;
-                    final @BiometricManager.Authenticators.Types int strength =
-                            Utils.propertyStrengthToAuthenticatorStrength(
-                                    hidlSensor.sensorStrength);
-                    final IrisAuthenticator authenticator = new IrisAuthenticator(mServiceWrapper,
-                            sensorId);
                     try {
-                        biometricService.registerAuthenticator(sensorId, TYPE_IRIS, strength,
-                                authenticator);
+                        biometricService.registerAuthenticator(TYPE_IRIS, hidlSensor,
+                                new IrisAuthenticator(mServiceWrapper, hidlSensor.sensorId));
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId);
+                        Slog.e(TAG, "Remote exception when registering sensorId: "
+                                + hidlSensor.sensorId);
                     }
                 }
             });
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b25206d..7e48f68 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -3391,6 +3391,7 @@
          * consistency of the Ikev2VpnRunner fields.
          */
         public void onDefaultNetworkChanged(@NonNull Network network) {
+            mEventChanges.log("[UnderlyingNW] Default network changed to " + network);
             Log.d(TAG, "onDefaultNetworkChanged: " + network);
 
             // If there is a new default network brought up, cancel the retry task to prevent
@@ -3628,6 +3629,7 @@
                 mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
                         .setTransportInfo(info)
                         .build();
+                mEventChanges.log("[VPNRunner] Update agent caps " + mNetworkCapabilities);
                 doSendNetworkCapabilities(mNetworkAgent, mNetworkCapabilities);
             }
         }
@@ -3664,6 +3666,7 @@
 
         private void startIkeSession(@NonNull Network underlyingNetwork) {
             Log.d(TAG, "Start new IKE session on network " + underlyingNetwork);
+            mEventChanges.log("[IKE] Start IKE session over " + underlyingNetwork);
 
             try {
                 // Clear mInterface to prevent Ikev2VpnRunner being cleared when
@@ -3778,6 +3781,7 @@
         }
 
         public void onValidationStatus(int status) {
+            mEventChanges.log("[Validation] validation status " + status);
             if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
                 // No data stall now. Reset it.
                 mExecutor.execute(() -> {
@@ -3818,6 +3822,7 @@
          * consistency of the Ikev2VpnRunner fields.
          */
         public void onDefaultNetworkLost(@NonNull Network network) {
+            mEventChanges.log("[UnderlyingNW] Network lost " + network);
             // If the default network is torn down, there is no need to call
             // startOrMigrateIkeSession() since it will always check if there is an active network
             // can be used or not.
@@ -3936,6 +3941,8 @@
          * consistency of the Ikev2VpnRunner fields.
          */
         public void onSessionLost(int token, @Nullable Exception exception) {
+            mEventChanges.log("[IKE] Session lost on network " + mActiveNetwork
+                    + (null == exception ? "" : " reason " + exception.getMessage()));
             Log.d(TAG, "onSessionLost() called for token " + token);
 
             if (!isActiveToken(token)) {
@@ -4092,6 +4099,7 @@
          * consistency of the Ikev2VpnRunner fields.
          */
         private void disconnectVpnRunner() {
+            mEventChanges.log("[VPNRunner] Disconnect runner, underlying network" + mActiveNetwork);
             mActiveNetwork = null;
             mUnderlyingNetworkCapabilities = null;
             mUnderlyingLinkProperties = null;
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index c05a03e..c76ca2b 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -286,7 +286,6 @@
                     final InputMethodSubtype.InputMethodSubtypeBuilder
                             builder = new InputMethodSubtype.InputMethodSubtypeBuilder()
                             .setSubtypeNameResId(label)
-                            .setSubtypeNameOverride(untranslatableName)
                             .setPhysicalKeyboardHint(
                                     pkLanguageTag == null ? null : new ULocale(pkLanguageTag),
                                     pkLayoutType == null ? "" : pkLayoutType)
@@ -302,6 +301,9 @@
                     if (subtypeId != InputMethodSubtype.SUBTYPE_ID_NONE) {
                         builder.setSubtypeId(subtypeId);
                     }
+                    if (untranslatableName != null) {
+                        builder.setSubtypeNameOverride(untranslatableName);
+                    }
                     tempSubtypesArray.add(builder.build());
                 }
             }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0da94ff6..ee4a6fe 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1516,11 +1516,6 @@
                 && !getSeparateProfileChallengeEnabledInternal(userId);
     }
 
-    private boolean isProfileWithSeparatedLock(int userId) {
-        return isCredentialSharableWithParent(userId)
-                && getSeparateProfileChallengeEnabledInternal(userId);
-    }
-
     /**
      * Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an
      * unlock operation.
@@ -2784,9 +2779,19 @@
 
         activateEscrowTokens(sp, userId);
 
-        if (isProfileWithSeparatedLock(userId)) {
-            setDeviceUnlockedForUser(userId);
+        if (isCredentialSharableWithParent(userId)) {
+            if (getSeparateProfileChallengeEnabledInternal(userId)) {
+                setDeviceUnlockedForUser(userId);
+            } else {
+                // Here only clear StrongAuthFlags for a profile that has a unified challenge.
+                // StrongAuth for a profile with a separate challenge is handled differently and
+                // is cleared after the user successfully confirms the separate challenge to enter
+                // the profile. StrongAuth for the full user (e.g. userId 0) is also handled
+                // separately by Keyguard.
+                mStrongAuth.reportUnlock(userId);
+            }
         }
+
         mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
 
         onSyntheticPasswordUnlocked(userId, sp);
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index c9ebeae..5015985 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -361,7 +361,8 @@
         }
         final int verifierUserId = verifierUser.getIdentifier();
 
-        List<String> requiredVerifierPackages = Arrays.asList(mPm.mRequiredVerifierPackages);
+        List<String> requiredVerifierPackages = new ArrayList<>(
+                Arrays.asList(mPm.mRequiredVerifierPackages));
         boolean requiredVerifierPackagesOverridden = false;
 
         // Allow verifier override for ADB installations which could already be unverified using
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8346e7c..9a8446a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7917,6 +7917,8 @@
 
         mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.mTaskId, requestedOrientation);
+
+        mDisplayContent.getDisplayRotation().onSetRequestedOrientation();
     }
 
     /*
@@ -9760,6 +9762,7 @@
      * directly with keeping its record.
      */
     void restartProcessIfVisible() {
+        if (finishing) return;
         Slog.i(TAG, "Request to restart process of " + this);
 
         // Reset the existing override configuration so it can be updated according to the latest
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d4f151f..38f13ec 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1606,6 +1606,8 @@
             transitionController.requestStartTransition(newTransition,
                     mTargetTask == null ? started.getTask() : mTargetTask,
                     remoteTransition, null /* displayChange */);
+        } else if (result == START_SUCCESS && mStartActivity.isState(RESUMED)) {
+            // Do nothing if the activity is started and is resumed directly.
         } else if (isStarted) {
             // Make the collecting transition wait until this request is ready.
             transitionController.setReady(started, false);
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 48cf567..a49ecce 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -228,7 +228,7 @@
             if (mReady == ready) {
                 return;
             }
-            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
+            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready %b", mSyncId, ready);
             mReady = ready;
             if (!ready) return;
             mWm.mWindowPlacerLocked.requestTraversal();
@@ -305,8 +305,8 @@
         if (mActiveSyncs.size() != 0) {
             // We currently only support one sync at a time, so start a new SyncGroup when there is
             // another may cause issue.
-            ProtoLog.w(WM_DEBUG_SYNC_ENGINE,
-                    "SyncGroup %d: Started when there is other active SyncGroup", s.mSyncId);
+            Slog.e(TAG, "SyncGroup " + s.mSyncId
+                    + ": Started when there is other active SyncGroup");
         }
         mActiveSyncs.put(s.mSyncId, s);
         ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s",
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index be80b01..7b562b0 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -286,8 +286,8 @@
                                 currentActivity.getCustomAnimation(false/* open */);
                         if (customAppTransition != null) {
                             infoBuilder.setCustomAnimation(currentActivity.packageName,
-                                    customAppTransition.mExitAnim,
                                     customAppTransition.mEnterAnim,
+                                    customAppTransition.mExitAnim,
                                     customAppTransition.mBackgroundColor);
                         }
                     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bec58b8..c2bc459 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -775,6 +775,8 @@
      */
     DisplayWindowPolicyControllerHelper mDwpcHelper;
 
+    private final DisplayRotationReversionController mRotationReversionController;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -1204,6 +1206,7 @@
                 mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
                             /* checkDeviceConfig */ false)
                         ? new DisplayRotationCompatPolicy(this) : null;
+        mRotationReversionController = new DisplayRotationReversionController(this);
 
         mInputMonitor = new InputMonitor(mWmService, this);
         mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
@@ -1333,6 +1336,10 @@
                 .show(mA11yOverlayLayer);
     }
 
+    DisplayRotationReversionController getRotationReversionController() {
+        return mRotationReversionController;
+    }
+
     boolean isReady() {
         // The display is ready when the system and the individual display are both ready.
         return mWmService.mDisplayReady && mDisplayReady;
@@ -1711,9 +1718,14 @@
     }
 
     private boolean updateOrientation(boolean forceUpdate) {
+        final WindowContainer prevOrientationSource = mLastOrientationSource;
         final int orientation = getOrientation();
         // The last orientation source is valid only after getOrientation.
         final WindowContainer orientationSource = getLastOrientationSource();
+        if (orientationSource != prevOrientationSource
+                && mRotationReversionController.isRotationReversionEnabled()) {
+            mRotationReversionController.updateForNoSensorOverride();
+        }
         final ActivityRecord r =
                 orientationSource != null ? orientationSource.asActivityRecord() : null;
         if (r != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 628f4d3..20048ce 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -33,6 +33,9 @@
 import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
 import static com.android.server.wm.DisplayRotationProto.ROTATION;
 import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
+import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
+import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_HALF_FOLD;
+import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_NOSENSOR;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
@@ -97,6 +100,8 @@
     // config changes and unexpected jumps while folding the device to closed state.
     private static final int FOLDING_RECOMPUTE_CONFIG_DELAY_MS = 800;
 
+    private static final int ROTATION_UNDEFINED = -1;
+
     private static class RotationAnimationPair {
         @AnimRes
         int mEnter;
@@ -104,6 +109,9 @@
         int mExit;
     }
 
+    @Nullable
+    final FoldController mFoldController;
+
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private final DisplayPolicy mDisplayPolicy;
@@ -125,8 +133,6 @@
     private OrientationListener mOrientationListener;
     private StatusBarManagerInternal mStatusBarManagerInternal;
     private SettingsObserver mSettingsObserver;
-    @Nullable
-    private FoldController mFoldController;
     @NonNull
     private final DeviceStateController mDeviceStateController;
     @NonNull
@@ -189,6 +195,12 @@
      */
     private int mShowRotationSuggestions;
 
+    /**
+     * The most recent {@link Surface.Rotation} choice shown to the user for confirmation, or
+     * {@link #ROTATION_UNDEFINED}
+     */
+    private int mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
+
     private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1;
     private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0;
     private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1;
@@ -291,7 +303,11 @@
             if (mSupportAutoRotation && mContext.getResources().getBoolean(
                     R.bool.config_windowManagerHalfFoldAutoRotateOverride)) {
                 mFoldController = new FoldController();
+            } else {
+                mFoldController = null;
             }
+        } else {
+            mFoldController = null;
         }
     }
 
@@ -349,6 +365,11 @@
         return -1;
     }
 
+    @VisibleForTesting
+    boolean useDefaultSettingsProvider() {
+        return isDefaultDisplay;
+    }
+
     /**
      * Updates the configuration which may have different values depending on current user, e.g.
      * runtime resource overlay.
@@ -894,7 +915,8 @@
 
     @VisibleForTesting
     void setUserRotation(int userRotationMode, int userRotation) {
-        if (isDefaultDisplay) {
+        mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
+        if (useDefaultSettingsProvider()) {
             // We'll be notified via settings listener, so we don't need to update internal values.
             final ContentResolver res = mContext.getContentResolver();
             final int accelerometerRotation =
@@ -1613,6 +1635,17 @@
         }
     }
 
+    /**
+     * Called from {@link ActivityRecord#setRequestedOrientation(int)}
+     */
+    void onSetRequestedOrientation() {
+        if (mCompatPolicyForImmersiveApps == null
+                || mRotationChoiceShownToUserForConfirmation == ROTATION_UNDEFINED) {
+            return;
+        }
+        mOrientationListener.onProposedRotationChanged(mRotationChoiceShownToUserForConfirmation);
+    }
+
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "DisplayRotation");
         pw.println(prefix + "  mCurrentAppOrientation="
@@ -1839,7 +1872,7 @@
                 return false;
             }
             if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
-                return !(isTabletop ^ mTabletopRotations.contains(mRotation));
+                return isTabletop == mTabletopRotations.contains(mRotation);
             }
             return true;
         }
@@ -1863,14 +1896,17 @@
             return mDeviceState == DeviceStateController.DeviceState.OPEN
                     && !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
                     && mInHalfFoldTransition
-                    && mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
+                    && mDisplayContent.getRotationReversionController().isOverrideActive(
+                        REVERSION_TYPE_HALF_FOLD)
                     && mUserRotationMode
-                    == WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
+                        == WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
         }
 
         int revertOverriddenRotation() {
             int savedRotation = mHalfFoldSavedRotation;
             mHalfFoldSavedRotation = -1;
+            mDisplayContent.getRotationReversionController()
+                    .revertOverride(REVERSION_TYPE_HALF_FOLD);
             mInHalfFoldTransition = false;
             return savedRotation;
         }
@@ -1890,6 +1926,8 @@
                     && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
                 // The device has transitioned to HALF_FOLDED state: save the current rotation and
                 // update the device rotation.
+                mDisplayContent.getRotationReversionController().beforeOverrideApplied(
+                        REVERSION_TYPE_HALF_FOLD);
                 mHalfFoldSavedRotation = mRotation;
                 mDeviceState = newState;
                 // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
@@ -2012,9 +2050,11 @@
             mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
             dispatchProposedRotation(rotation);
             if (isRotationChoiceAllowed(rotation)) {
+                mRotationChoiceShownToUserForConfirmation = rotation;
                 final boolean isValid = isValidRotationChoice(rotation);
                 sendProposedRotationChangeToStatusBarInternal(rotation, isValid);
             } else {
+                mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
                 mService.updateRotation(false /* alwaysSendConfiguration */,
                         false /* forceRelayout */);
             }
@@ -2093,6 +2133,8 @@
             final int mHalfFoldSavedRotation;
             final boolean mInHalfFoldTransition;
             final DeviceStateController.DeviceState mDeviceState;
+            @Nullable final boolean[] mRotationReversionSlots;
+
             @Nullable final String mDisplayRotationCompatPolicySummary;
 
             Record(DisplayRotation dr, int fromRotation, int toRotation) {
@@ -2133,6 +2175,8 @@
                         ? null
                         : dc.mDisplayRotationCompatPolicy
                                 .getSummaryForDisplayRotationHistoryRecord();
+                mRotationReversionSlots =
+                        dr.mDisplayContent.getRotationReversionController().getSlotsCopy();
             }
 
             void dump(String prefix, PrintWriter pw) {
@@ -2158,6 +2202,12 @@
                 if (mDisplayRotationCompatPolicySummary != null) {
                     pw.println(prefix + mDisplayRotationCompatPolicySummary);
                 }
+                if (mRotationReversionSlots != null) {
+                    pw.println(prefix + " reversionSlots= NOSENSOR "
+                            + mRotationReversionSlots[REVERSION_TYPE_NOSENSOR] + ", CAMERA "
+                            + mRotationReversionSlots[REVERSION_TYPE_CAMERA_COMPAT] + " HALF_FOLD "
+                            + mRotationReversionSlots[REVERSION_TYPE_HALF_FOLD]);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index fb72d6c..ae93a94 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -33,6 +33,7 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -156,6 +157,11 @@
     @ScreenOrientation
     int getOrientation() {
         mLastReportedOrientation = getOrientationInternal();
+        if (mLastReportedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+            rememberOverriddenOrientationIfNeeded();
+        } else {
+            restoreOverriddenOrientationIfNeeded();
+        }
         return mLastReportedOrientation;
     }
 
@@ -277,6 +283,34 @@
                 + " }";
     }
 
+    private void restoreOverriddenOrientationIfNeeded() {
+        if (!isOrientationOverridden()) {
+            return;
+        }
+        if (mDisplayContent.getRotationReversionController().revertOverride(
+                REVERSION_TYPE_CAMERA_COMPAT)) {
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Reverting orientation after camera compat force rotation");
+            // Reset last orientation source since we have reverted the orientation.
+            mDisplayContent.mLastOrientationSource = null;
+        }
+    }
+
+    private boolean isOrientationOverridden() {
+        return mDisplayContent.getRotationReversionController().isOverrideActive(
+                REVERSION_TYPE_CAMERA_COMPAT);
+    }
+
+    private void rememberOverriddenOrientationIfNeeded() {
+        if (!isOrientationOverridden()) {
+            mDisplayContent.getRotationReversionController().beforeOverrideApplied(
+                    REVERSION_TYPE_CAMERA_COMPAT);
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Saving original orientation before camera compat, last orientation is %d",
+                    mDisplayContent.getLastOrientation());
+        }
+    }
+
     // Refreshing only when configuration changes after rotation.
     private boolean shouldRefreshActivity(ActivityRecord activity, Configuration newConfig,
             Configuration lastReportedConfig) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
new file mode 100644
index 0000000..d3a8a82
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.content.ActivityInfoProto;
+import android.view.Surface;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.policy.WindowManagerPolicy;
+
+/**
+ * Defines the behavior of reversion from device rotation overrides.
+ *
+ * <p>There are 3 override types:
+ * <ol>
+ *  <li>The top application has {@link SCREEN_ORIENTATION_NOSENSOR} set and is rotated to
+ *  {@link ROTATION_0}.
+ *  <li>Camera compat treatment has rotated the app {@link DisplayRotationCompatPolicy}.
+ *  <li>The device is half-folded and has auto-rotate is temporarily enabled.
+ * </ol>
+ *
+ * <p>Before an override is enabled, a component should call {@code beforeOverrideApplied}. When
+ * it wishes to revert, it should call {@code revertOverride}. The user rotation will be restored
+ * if there are no other overrides present.
+ */
+final class DisplayRotationReversionController {
+
+    static final int REVERSION_TYPE_NOSENSOR = 0;
+    static final int REVERSION_TYPE_CAMERA_COMPAT = 1;
+    static final int REVERSION_TYPE_HALF_FOLD = 2;
+    private static final int NUM_SLOTS = 3;
+
+    @Surface.Rotation
+    private int mUserRotationOverridden = WindowConfiguration.ROTATION_UNDEFINED;
+    @WindowManagerPolicy.UserRotationMode
+    private int mUserRotationModeOverridden;
+
+    private final boolean[] mSlots = new boolean[NUM_SLOTS];
+    private final DisplayContent mDisplayContent;
+
+    DisplayRotationReversionController(DisplayContent content) {
+        mDisplayContent = content;
+    }
+
+    boolean isRotationReversionEnabled() {
+        return mDisplayContent.mDisplayRotationCompatPolicy != null
+                || mDisplayContent.getDisplayRotation().mFoldController != null
+                || mDisplayContent.getIgnoreOrientationRequest();
+    }
+
+    void beforeOverrideApplied(int slotIndex) {
+        if (mSlots[slotIndex]) return;
+        maybeSaveUserRotation();
+        mSlots[slotIndex] = true;
+    }
+
+    boolean isOverrideActive(int slotIndex) {
+        return mSlots[slotIndex];
+    }
+
+    @Nullable
+    boolean[] getSlotsCopy() {
+        return isRotationReversionEnabled() ? mSlots.clone() : null;
+    }
+
+    void updateForNoSensorOverride() {
+        if (!mSlots[REVERSION_TYPE_NOSENSOR]) {
+            if (isTopFullscreenActivityNoSensor()) {
+                ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override detected");
+                beforeOverrideApplied(REVERSION_TYPE_NOSENSOR);
+            }
+        } else {
+            if (!isTopFullscreenActivityNoSensor()) {
+                ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override is absent: reverting");
+                revertOverride(REVERSION_TYPE_NOSENSOR);
+            }
+        }
+    }
+
+    boolean isAnyOverrideActive() {
+        for (int i = 0; i < NUM_SLOTS; ++i) {
+            if (mSlots[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean revertOverride(int slotIndex) {
+        if (!mSlots[slotIndex]) return false;
+        mSlots[slotIndex] = false;
+        if (isAnyOverrideActive()) {
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Other orientation overrides are in place: not reverting");
+            return false;
+        }
+        // Only override if the rotation is frozen and there are no other active slots.
+        if (mDisplayContent.getDisplayRotation().isRotationFrozen()) {
+            mDisplayContent.getDisplayRotation().setUserRotation(
+                    mUserRotationModeOverridden,
+                    mUserRotationOverridden);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void maybeSaveUserRotation() {
+        if (!isAnyOverrideActive()) {
+            mUserRotationModeOverridden =
+                    mDisplayContent.getDisplayRotation().getUserRotationMode();
+            mUserRotationOverridden = mDisplayContent.getDisplayRotation().getUserRotation();
+        }
+    }
+
+    private boolean isTopFullscreenActivityNoSensor() {
+        final Task topFullscreenTask =
+                mDisplayContent.getTask(
+                        t -> t.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
+        if (topFullscreenTask != null) {
+            final ActivityRecord topActivity =
+                    topFullscreenTask.topRunningActivity();
+            return topActivity != null && topActivity.getOrientation()
+                    == ActivityInfoProto.SCREEN_ORIENTATION_NOSENSOR;
+        }
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 93233dd..ff1deaf 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1597,11 +1597,10 @@
         inheritConfiguration(firstOpaqueActivityBeneath);
         mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
                 mActivityRecord, firstOpaqueActivityBeneath,
-                (opaqueConfig, transparentConfig) -> {
-                    final Configuration mutatedConfiguration =
-                            fromOriginalTranslucentConfig(transparentConfig);
+                (opaqueConfig, transparentOverrideConfig) -> {
+                    resetTranslucentOverrideConfig(transparentOverrideConfig);
                     final Rect parentBounds = parent.getWindowConfiguration().getBounds();
-                    final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds();
+                    final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds();
                     final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
                     // We cannot use letterboxBounds directly here because the position relies on
                     // letterboxing. Using letterboxBounds directly, would produce a double offset.
@@ -1610,9 +1609,9 @@
                             parentBounds.top + letterboxBounds.height());
                     // We need to initialize appBounds to avoid NPE. The actual value will
                     // be set ahead when resolving the Configuration for the activity.
-                    mutatedConfiguration.windowConfiguration.setAppBounds(new Rect());
+                    transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect());
                     inheritConfiguration(firstOpaqueActivityBeneath);
-                    return mutatedConfiguration;
+                    return transparentOverrideConfig;
                 });
     }
 
@@ -1691,20 +1690,16 @@
                 true /* traverseTopToBottom */));
     }
 
-    // When overriding translucent activities configuration we need to keep some of the
-    // original properties
-    private Configuration fromOriginalTranslucentConfig(Configuration translucentConfig) {
-        final Configuration configuration = new Configuration(translucentConfig);
+    /** Resets the screen size related fields so they can be resolved by requested bounds later. */
+    private static void resetTranslucentOverrideConfig(Configuration config) {
         // The values for the following properties will be defined during the configuration
         // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the
         // properties inherited from the first not finishing opaque activity beneath.
-        configuration.orientation = ORIENTATION_UNDEFINED;
-        configuration.screenWidthDp = configuration.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
-        configuration.screenHeightDp =
-                configuration.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
-        configuration.smallestScreenWidthDp =
-                configuration.compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-        return configuration;
+        config.orientation = ORIENTATION_UNDEFINED;
+        config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
+        config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
+        config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp =
+                SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
     }
 
     private void inheritConfiguration(ActivityRecord firstOpaque) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f8f0211..f5079d3 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1512,7 +1512,7 @@
         // callbacks here.
         final Task removedTask = mTasks.remove(removeIndex);
         if (removedTask != task) {
-            if (removedTask.hasChild()) {
+            if (removedTask.hasChild() && !removedTask.isActivityTypeHome()) {
                 Slog.i(TAG, "Add " + removedTask + " to hidden list because adding " + task);
                 // A non-empty task is replaced by a new task. Because the removed task is no longer
                 // managed by the recent tasks list, add it to the hidden list to prevent the task
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0857898..73f4b5b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4687,7 +4687,7 @@
         if (!isAttached()) {
             return;
         }
-        mTransitionController.collect(this);
+        mTransitionController.recordTaskOrder(this);
 
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3cc1548..e209ef9 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -521,10 +521,7 @@
             mChanges.put(wc, info);
         }
         mParticipants.add(wc);
-        if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
-            mTargetDisplays.add(wc.getDisplayContent());
-            addOnTopTasks(wc.getDisplayContent(), mOnTopTasksStart);
-        }
+        recordDisplay(wc.getDisplayContent());
         if (info.mShowWallpaper) {
             // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
             final WindowState wallpaper =
@@ -535,6 +532,20 @@
         }
     }
 
+    private void recordDisplay(DisplayContent dc) {
+        if (dc == null || mTargetDisplays.contains(dc)) return;
+        mTargetDisplays.add(dc);
+        addOnTopTasks(dc, mOnTopTasksStart);
+    }
+
+    /**
+     * Records information about the initial task order. This does NOT collect anything. Call this
+     * before any ordering changes *could* occur, but it is not known yet if it will occur.
+     */
+    void recordTaskOrder(WindowContainer from) {
+        recordDisplay(from.getDisplayContent());
+    }
+
     /** Adds the top non-alwaysOnTop tasks within `task` to `out`. */
     private static void addOnTopTasks(Task task, ArrayList<Task> out) {
         for (int i = task.getChildCount() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bcb8c46..fc59b85 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -605,6 +605,12 @@
         mCollectingTransition.collectExistenceChange(wc);
     }
 
+    /** @see Transition#recordTaskOrder */
+    void recordTaskOrder(@NonNull WindowContainer wc) {
+        if (mCollectingTransition == null) return;
+        mCollectingTransition.recordTaskOrder(wc);
+    }
+
     /**
      * Collects the window containers which need to be synced with the changing display area into
      * the current collecting transition.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4117641..cf6efd2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -4047,7 +4047,7 @@
                 final Configuration mergedConfiguration =
                         configurationMerger != null
                                 ? configurationMerger.merge(mergedOverrideConfig,
-                                receiver.getConfiguration())
+                                        receiver.getRequestedOverrideConfiguration())
                                 : supplier.getConfiguration();
                 receiver.onRequestedOverrideConfigurationChanged(mergedConfiguration);
             }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 9320dd2..06b96eb 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -796,7 +796,7 @@
                 return DeviceConfig.getBoolean(
                         DeviceConfig.NAMESPACE_CREDENTIAL,
                         DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
-                        false);
+                        true);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 318067e..8211d6f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -406,7 +406,7 @@
         assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
         assertTrue(queue.isRunnable());
         assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
-        assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
+        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
     }
 
     /**
@@ -434,13 +434,13 @@
         queue.setProcessAndUidCached(null, false);
         assertTrue(queue.isRunnable());
         assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
-        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
+        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
         assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);
 
         queue.setProcessAndUidCached(null, true);
         assertTrue(queue.isRunnable());
         assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
-        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
+        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
         assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);
     }
 
@@ -1154,6 +1154,41 @@
                 times(1));
     }
 
+    @Test
+    public void testGetPreferredSchedulingGroup() throws Exception {
+        final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+                PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
+
+        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
+
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(timeTick,
+                List.of(makeMockRegisteredReceiver())), 0, false);
+        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
+
+        // Make the foreground broadcast as active.
+        queue.makeActiveNextPending();
+        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
+
+        queue.makeActiveIdle();
+        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(airplane,
+                List.of(makeMockRegisteredReceiver())), 0, false);
+
+        // Make the background broadcast as active.
+        queue.makeActiveNextPending();
+        assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
+
+        queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(timeTick,
+                List.of(makeMockRegisteredReceiver())), 0, false);
+        // Even though the active broadcast is not a foreground one, scheduling group will be
+        // DEFAULT since there is a foreground broadcast waiting to be delivered.
+        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
+    }
+
     private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
         final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
         packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 6216c66..4b86dd0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -152,8 +152,7 @@
 
         verify(mBiometricService, never()).registerAuthenticator(
                 anyInt(),
-                anyInt(),
-                anyInt(),
+                any(),
                 any());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 4cdca26..dbf5021 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -44,13 +44,14 @@
 import android.app.trust.ITrustManager;
 import android.content.Context;
 import android.hardware.biometrics.BiometricManager.Authenticators;
-import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorProperties;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Binder;
@@ -458,9 +459,16 @@
         IBiometricAuthenticator fingerprintAuthenticator = mock(IBiometricAuthenticator.class);
         when(fingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
         when(fingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
-        mSensors.add(new BiometricSensor(mContext, id,
+
+        final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
+                id, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+                List.of() /* componentInfo */, type,
+                false /* resetLockoutRequiresHardwareAuthToken */);
+        mFingerprintSensorProps.add(props);
+
+        mSensors.add(new BiometricSensor(mContext,
                 TYPE_FINGERPRINT /* modality */,
-                Authenticators.BIOMETRIC_STRONG /* strength */,
+                props,
                 fingerprintAuthenticator) {
             @Override
             boolean confirmationAlwaysRequired(int userId) {
@@ -473,21 +481,6 @@
             }
         });
 
-        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
-        componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
-                "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                "00000001" /* serialNumber */, "" /* softwareVersion */));
-        componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
-                "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                "vendor/version/revision" /* softwareVersion */));
-
-        mFingerprintSensorProps.add(new FingerprintSensorPropertiesInternal(id,
-                SensorProperties.STRENGTH_STRONG,
-                5 /* maxEnrollmentsPerUser */,
-                componentInfo,
-                type,
-                false /* resetLockoutRequiresHardwareAuthToken */));
-
         when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
     }
 
@@ -495,9 +488,13 @@
             IBiometricAuthenticator authenticator) throws RemoteException {
         when(authenticator.isHardwareDetected(any())).thenReturn(true);
         when(authenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
-        mSensors.add(new BiometricSensor(mContext, id,
+        mSensors.add(new BiometricSensor(mContext,
                 TYPE_FACE /* modality */,
-                Authenticators.BIOMETRIC_STRONG /* strength */,
+                new FaceSensorPropertiesInternal(id,
+                        SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+                        List.of() /* componentInfo */, FaceSensorProperties.TYPE_UNKNOWN,
+                        true /* supportsFace Detection */, true /* supportsSelfIllumination */,
+                        false /* resetLockoutRequiresHardwareAuthToken */),
                 authenticator) {
             @Override
             boolean confirmationAlwaysRequired(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 168642e..b51a8c4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -19,6 +19,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI;
@@ -66,7 +67,11 @@
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -93,6 +98,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
 import java.util.Random;
 
 @Presubmit
@@ -114,6 +120,7 @@
 
     private static final int SENSOR_ID_FINGERPRINT = 0;
     private static final int SENSOR_ID_FACE = 1;
+    private FingerprintSensorPropertiesInternal mFingerprintProps;
 
     private BiometricService mBiometricService;
 
@@ -193,6 +200,11 @@
         };
 
         when(mInjector.getConfiguration(any())).thenReturn(config);
+
+        mFingerprintProps = new FingerprintSensorPropertiesInternal(SENSOR_ID_FINGERPRINT,
+                STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                FingerprintSensorProperties.TYPE_UNKNOWN,
+                false /* resetLockoutRequiresHardwareAuthToken */);
     }
 
     @Test
@@ -328,8 +340,7 @@
 
         mBiometricService = new BiometricService(mContext, mInjector);
         mBiometricService.onStart();
-        mBiometricService.mImpl.registerAuthenticator(0 /* id */,
-                TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+        mBiometricService.mImpl.registerAuthenticator(TYPE_FINGERPRINT, mFingerprintProps,
                 mFingerprintAuthenticator);
 
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
@@ -401,8 +412,7 @@
 
         mBiometricService = new BiometricService(mContext, mInjector);
         mBiometricService.onStart();
-        mBiometricService.mImpl.registerAuthenticator(0 /* id */,
-                TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+        mBiometricService.mImpl.registerAuthenticator(TYPE_FINGERPRINT, mFingerprintProps,
                 mFingerprintAuthenticator);
 
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
@@ -1334,9 +1344,13 @@
 
         for (int i = 0; i < testCases.length; i++) {
             final BiometricSensor sensor =
-                    new BiometricSensor(mContext, 0 /* id */,
+                    new BiometricSensor(mContext,
                             TYPE_FINGERPRINT,
-                            testCases[i][0],
+                            new FingerprintSensorPropertiesInternal(i /* id */,
+                                    Utils.authenticatorStrengthToPropertyStrength(testCases[i][0]),
+                                    5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                                    FingerprintSensorProperties.TYPE_UNKNOWN,
+                                    false /* resetLockoutRequiresHardwareAuthToken */),
                             mock(IBiometricAuthenticator.class)) {
                         @Override
                         boolean confirmationAlwaysRequired(int userId) {
@@ -1364,8 +1378,7 @@
         when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
                 .thenReturn(true);
         when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
-        mBiometricService.mImpl.registerAuthenticator(0 /* testId */,
-                TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+        mBiometricService.mImpl.registerAuthenticator(TYPE_FINGERPRINT, mFingerprintProps,
                 mFingerprintAuthenticator);
 
         verify(mBiometricService.mBiometricStrengthController).updateStrengths();
@@ -1376,15 +1389,14 @@
         mBiometricService = new BiometricService(mContext, mInjector);
         mBiometricService.onStart();
 
-        final int testId = 0;
-
         when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
 
         when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
                 .thenReturn(true);
         when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
-        mBiometricService.mImpl.registerAuthenticator(testId /* id */,
-                TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+
+        final int testId = SENSOR_ID_FINGERPRINT;
+        mBiometricService.mImpl.registerAuthenticator(TYPE_FINGERPRINT, mFingerprintProps,
                 mFingerprintAuthenticator);
 
         // Downgrade the authenticator
@@ -1484,11 +1496,9 @@
         mBiometricService.onStart();
 
         mBiometricService.mImpl.registerAuthenticator(
-                0 /* id */, 2 /* modality */, 15 /* strength */,
-                mFingerprintAuthenticator);
+                2 /* modality */, mFingerprintProps, mFingerprintAuthenticator);
         mBiometricService.mImpl.registerAuthenticator(
-                0 /* id */, 2 /* modality */, 15 /* strength */,
-                mFingerprintAuthenticator);
+                2 /* modality */, mFingerprintProps, mFingerprintAuthenticator);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -1498,9 +1508,7 @@
         mBiometricService.onStart();
 
         mBiometricService.mImpl.registerAuthenticator(
-                0 /* id */, 2 /* modality */,
-                Authenticators.BIOMETRIC_STRONG /* strength */,
-                null /* authenticator */);
+                2 /* modality */, mFingerprintProps, null /* authenticator */);
     }
 
     @Test
@@ -1511,8 +1519,13 @@
 
         for (String s : mInjector.getConfiguration(null)) {
             SensorConfig config = new SensorConfig(s);
-            mBiometricService.mImpl.registerAuthenticator(config.id, config.modality,
-                config.strength, mFingerprintAuthenticator);
+            mBiometricService.mImpl.registerAuthenticator(config.modality,
+                    new FingerprintSensorPropertiesInternal(config.id,
+                            Utils.authenticatorStrengthToPropertyStrength(config.strength),
+                            5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                            FingerprintSensorProperties.TYPE_UNKNOWN,
+                            false /* resetLockoutRequiresHardwareAuthToken */),
+                    mFingerprintAuthenticator);
         }
     }
 
@@ -1609,7 +1622,12 @@
             when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
             when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
                     .thenReturn(LockoutTracker.LOCKOUT_NONE);
-            mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FINGERPRINT, modality, strength,
+            mBiometricService.mImpl.registerAuthenticator(modality,
+                    new FingerprintSensorPropertiesInternal(SENSOR_ID_FINGERPRINT,
+                            Utils.authenticatorStrengthToPropertyStrength(strength),
+                            5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                            FingerprintSensorProperties.TYPE_UNKNOWN,
+                            false /* resetLockoutRequiresHardwareAuthToken */),
                     mFingerprintAuthenticator);
         }
 
@@ -1618,7 +1636,13 @@
             when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
             when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
                     .thenReturn(LockoutTracker.LOCKOUT_NONE);
-            mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FACE, modality, strength,
+            mBiometricService.mImpl.registerAuthenticator(modality,
+                    new FaceSensorPropertiesInternal(SENSOR_ID_FACE,
+                            Utils.authenticatorStrengthToPropertyStrength(strength),
+                            5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                            FaceSensorProperties.TYPE_UNKNOWN, true /* supportsFace Detection */,
+                            true /* supportsSelfIllumination */,
+                            false /* resetLockoutRequiresHardwareAuthToken */),
                     mFaceAuthenticator);
         }
     }
@@ -1641,15 +1665,27 @@
                 when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
                         .thenReturn(true);
                 when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
-                mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FINGERPRINT, modality,
-                        strength, mFingerprintAuthenticator);
+                mBiometricService.mImpl.registerAuthenticator(modality,
+                        new FingerprintSensorPropertiesInternal(SENSOR_ID_FINGERPRINT,
+                                Utils.authenticatorStrengthToPropertyStrength(strength),
+                                5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                                FingerprintSensorProperties.TYPE_UNKNOWN,
+                                false /* resetLockoutRequiresHardwareAuthToken */),
+                        mFingerprintAuthenticator);
             }
 
             if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
                 when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
                 when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
-                mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FACE, modality,
-                        strength, mFaceAuthenticator);
+                mBiometricService.mImpl.registerAuthenticator(modality,
+                        new FaceSensorPropertiesInternal(SENSOR_ID_FACE,
+                                Utils.authenticatorStrengthToPropertyStrength(strength),
+                                5 /* maxEnrollmentsPerUser */, List.of() /* componentInfo */,
+                                FaceSensorProperties.TYPE_UNKNOWN,
+                                true /* supportsFace Detection */,
+                                true /* supportsSelfIllumination */,
+                                false /* resetLockoutRequiresHardwareAuthToken */),
+                        mFaceAuthenticator);
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java
index ee5ab92..f7539bd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.biometrics;
 
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -27,9 +30,13 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IInvalidationCallback;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -42,6 +49,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.List;
 
 @Presubmit
 @SmallTest
@@ -59,26 +67,54 @@
     public void testCallbackReceived_whenAllStrongSensorsInvalidated() throws Exception {
         final IBiometricAuthenticator authenticator1 = mock(IBiometricAuthenticator.class);
         when(authenticator1.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
-        final TestSensor sensor1 = new TestSensor(mContext, 0 /* id */,
-                BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+        final TestSensor sensor1 = new TestSensor(mContext,
+                BiometricAuthenticator.TYPE_FINGERPRINT,
+                new FingerprintSensorPropertiesInternal(0 /* id */,
+                        STRENGTH_STRONG,
+                        5 /* maxEnrollmentsPerUser */,
+                        List.of() /* componentInfo */,
+                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+                        false /* resetLockoutRequiresHardwareAuthToken */),
                 authenticator1);
 
         final IBiometricAuthenticator authenticator2 = mock(IBiometricAuthenticator.class);
         when(authenticator2.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
-        final TestSensor sensor2 = new TestSensor(mContext, 1 /* id */,
-                BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+        final TestSensor sensor2 = new TestSensor(mContext,
+                BiometricAuthenticator.TYPE_FINGERPRINT,
+                new FingerprintSensorPropertiesInternal(1 /* id */,
+                        STRENGTH_STRONG,
+                        5 /* maxEnrollmentsPerUser */,
+                        List.of() /* componentInfo */,
+                        FingerprintSensorProperties.TYPE_REAR,
+                        false /* resetLockoutRequiresHardwareAuthToken */),
                 authenticator2);
 
         final IBiometricAuthenticator authenticator3 = mock(IBiometricAuthenticator.class);
         when(authenticator3.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
-        final TestSensor sensor3 = new TestSensor(mContext, 2 /* id */,
-                BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG,
+        final TestSensor sensor3 = new TestSensor(mContext,
+                BiometricAuthenticator.TYPE_FACE,
+                new FaceSensorPropertiesInternal(2 /* id */,
+                        STRENGTH_STRONG,
+                        5 /* maxEnrollmentsPerUser */,
+                        List.of() /* componentInfo */,
+                        FaceSensorProperties.TYPE_RGB,
+                        true /* supportsFace Detection */,
+                        true /* supportsSelfIllumination */,
+                        false /* resetLockoutRequiresHardwareAuthToken */),
                 authenticator3);
 
         final IBiometricAuthenticator authenticator4 = mock(IBiometricAuthenticator.class);
         when(authenticator4.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
-        final TestSensor sensor4 = new TestSensor(mContext, 3 /* id */,
-                BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK,
+        final TestSensor sensor4 = new TestSensor(mContext,
+                BiometricAuthenticator.TYPE_FACE,
+                new FaceSensorPropertiesInternal(3 /* id */,
+                        STRENGTH_WEAK,
+                        5 /* maxEnrollmentsPerUser */,
+                        List.of() /* componentInfo */,
+                        FaceSensorProperties.TYPE_IR,
+                        true /* supportsFace Detection */,
+                        true /* supportsSelfIllumination */,
+                        false /* resetLockoutRequiresHardwareAuthToken */),
                 authenticator4);
 
         final ArrayList<BiometricSensor> sensors = new ArrayList<>();
@@ -113,9 +149,9 @@
 
     private static class TestSensor extends BiometricSensor {
 
-        TestSensor(@NonNull Context context, int id, int modality, int strength,
+        TestSensor(@NonNull Context context, int modality, @NonNull SensorPropertiesInternal props,
                 @NonNull IBiometricAuthenticator impl) {
-            super(context, id, modality, strength, impl);
+            super(context, modality, props, impl);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
index 903ed90..d3f04df 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
@@ -17,8 +17,6 @@
 package com.android.server.biometrics.sensors.face;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
-import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
 
@@ -70,9 +68,7 @@
     @Mock
     private ServiceProvider mProvider2;
     @Captor
-    private ArgumentCaptor<Integer> mIdCaptor;
-    @Captor
-    private ArgumentCaptor<Integer> mStrengthCaptor;
+    private ArgumentCaptor<FaceSensorPropertiesInternal> mPropsCaptor;
 
     private FaceSensorPropertiesInternal mProvider1Props;
     private FaceSensorPropertiesInternal mProvider2Props;
@@ -82,13 +78,13 @@
     public void setup() {
         mProvider1Props = new FaceSensorPropertiesInternal(SENSOR_ID_1,
                 STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */,
-                List.of(), FaceSensorProperties.TYPE_RGB,
+                List.of() /* componentInfo */, FaceSensorProperties.TYPE_RGB,
                 true /* supportsFace Detection */,
                 true /* supportsSelfIllumination */,
                 false /* resetLockoutRequiresHardwareAuthToken */);
         mProvider2Props = new FaceSensorPropertiesInternal(SENSOR_ID_2,
                 STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
-                List.of(), FaceSensorProperties.TYPE_IR,
+                List.of() /* componentInfo */, FaceSensorProperties.TYPE_IR,
                 true /* supportsFace Detection */,
                 true /* supportsSelfIllumination */,
                 false /* resetLockoutRequiresHardwareAuthToken */);
@@ -107,10 +103,9 @@
         assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2);
         assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props);
         verify(mBiometricService, times(2)).registerAuthenticator(
-                mIdCaptor.capture(), eq(TYPE_FACE), mStrengthCaptor.capture(), any());
-        assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2);
-        assertThat(mStrengthCaptor.getAllValues())
-                .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG);
+                eq(TYPE_FACE), mPropsCaptor.capture(), any());
+        assertThat(mPropsCaptor.getAllValues())
+                .containsExactly(mProvider1Props, mProvider2Props);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
index 13c3f64..6e09069 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
@@ -17,8 +17,6 @@
 package com.android.server.biometrics.sensors.fingerprint;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
-import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
 
@@ -70,9 +68,7 @@
     @Mock
     private ServiceProvider mProvider2;
     @Captor
-    private ArgumentCaptor<Integer> mIdCaptor;
-    @Captor
-    private ArgumentCaptor<Integer> mStrengthCaptor;
+    private ArgumentCaptor<FingerprintSensorPropertiesInternal> mPropsCaptor;
 
     private FingerprintSensorPropertiesInternal mProvider1Props;
     private FingerprintSensorPropertiesInternal mProvider2Props;
@@ -82,11 +78,11 @@
     public void setup() {
         mProvider1Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_1,
                 STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */,
-                List.of(), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+                List.of() /* componentInfo */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
                 false /* resetLockoutRequiresHardwareAuthToken */);
         mProvider2Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_2,
                 STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
-                List.of(), FingerprintSensorProperties.TYPE_UNKNOWN,
+                List.of() /* componentInfo */, FingerprintSensorProperties.TYPE_UNKNOWN,
                 false /* resetLockoutRequiresHardwareAuthToken */);
 
         when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props));
@@ -103,10 +99,9 @@
         assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2);
         assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props);
         verify(mBiometricService, times(2)).registerAuthenticator(
-                mIdCaptor.capture(), eq(TYPE_FINGERPRINT), mStrengthCaptor.capture(), any());
-        assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2);
-        assertThat(mStrengthCaptor.getAllValues())
-                .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG);
+                eq(TYPE_FINGERPRINT), mPropsCaptor.capture(), any());
+        assertThat(mPropsCaptor.getAllValues())
+                .containsExactly(mProvider1Props, mProvider2Props);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index 25a700a..1089c07 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -110,15 +110,17 @@
     private final FingerprintSensorPropertiesInternal mSensorPropsDefault =
             new FingerprintSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG,
                     2 /* maxEnrollmentsPerUser */,
-                    List.of(),
+                    List.of() /* componentInfo */,
                     TYPE_REAR,
                     false /* resetLockoutRequiresHardwareAuthToken */);
     private final FingerprintSensorPropertiesInternal mSensorPropsVirtual =
             new FingerprintSensorPropertiesInternal(ID_VIRTUAL, STRENGTH_STRONG,
                     2 /* maxEnrollmentsPerUser */,
-                    List.of(),
+                    List.of() /* componentInfo */,
                     TYPE_UDFPS_OPTICAL,
                     false /* resetLockoutRequiresHardwareAuthToken */);
+    @Captor
+    private ArgumentCaptor<FingerprintSensorPropertiesInternal> mPropsCaptor;
     private FingerprintService mService;
 
     @Before
@@ -166,7 +168,8 @@
         mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
         waitForRegistration();
 
-        verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
+        verify(mIBiometricService).registerAuthenticator(anyInt(), mPropsCaptor.capture(), any());
+        assertThat(mPropsCaptor.getAllValues()).containsExactly(mSensorPropsDefault);
     }
 
     @Test
@@ -178,7 +181,8 @@
         mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
         waitForRegistration();
 
-        verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
+        verify(mIBiometricService).registerAuthenticator(anyInt(), mPropsCaptor.capture(), any());
+        assertThat(mPropsCaptor.getAllValues()).containsExactly(mSensorPropsVirtual);
     }
 
     @Test
@@ -188,7 +192,8 @@
         mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
         waitForRegistration();
 
-        verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
+        verify(mIBiometricService).registerAuthenticator(anyInt(), mPropsCaptor.capture(), any());
+        assertThat(mPropsCaptor.getAllValues()).containsExactly(mSensorPropsVirtual);
     }
 
     private void waitForRegistration() throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index 82bc6f6..06fc017 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -87,6 +87,7 @@
         Mockito.doReturn(new Intent()).when(mContext).registerReceiverAsUser(
                 any(), any(), any(), any(), any());
         Mockito.doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
+        Mockito.doReturn(new Intent()).when(mContext).registerReceiver(any(), any(), anyInt());
         Mockito.doNothing().when(mContext).unregisterReceiver(any());
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index b1a9f08..34bb664 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -96,8 +96,6 @@
     private TestableNotificationManagerService mService;
     private NotificationManagerService.RoleObserver mRoleObserver;
 
-    private TestableContext mContext = spy(getContext());
-
     @Mock
     private PreferencesHelper mPreferencesHelper;
     @Mock
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index b8a21ec..341b331 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -589,12 +589,18 @@
                 throw new IllegalStateException("Orientation in new config should be either"
                         + "landscape or portrait.");
         }
+
+        final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation();
+        spyOn(displayRotation);
+
         activity.setRequestedOrientation(requestedOrientation);
 
         final ActivityConfigurationChangeItem expected =
                 ActivityConfigurationChangeItem.obtain(newConfig);
         verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
                 eq(activity.token), eq(expected));
+
+        verify(displayRotation).onSetRequestedOrientation();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ba9f809..7330411 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -24,6 +24,7 @@
 import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
@@ -1063,6 +1064,51 @@
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
     }
 
+    private void updateAllDisplayContentAndRotation(DisplayContent dc) {
+        // NB updateOrientation will not revert the user orientation until a settings change
+        // takes effect.
+        dc.updateOrientation();
+        dc.onDisplayChanged(dc);
+        dc.mWmService.updateRotation(true /* alwaysSendConfiguration */,
+                false /* forceRelayout */);
+        waitUntilHandlersIdle();
+    }
+
+    @Test
+    public void testNoSensorRevert() {
+        final DisplayContent dc = mDisplayContent;
+        spyOn(dc);
+        doReturn(true).when(dc).getIgnoreOrientationRequest();
+        final DisplayRotation dr = dc.getDisplayRotation();
+        spyOn(dr);
+        doReturn(false).when(dr).useDefaultSettingsProvider();
+        final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        app.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, app);
+
+        assertFalse(dc.getRotationReversionController().isAnyOverrideActive());
+        dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED,
+                ROTATION_90);
+        updateAllDisplayContentAndRotation(dc);
+        assertEquals(ROTATION_90, dc.getDisplayRotation()
+                .rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_90));
+
+        app.setOrientation(SCREEN_ORIENTATION_NOSENSOR);
+        updateAllDisplayContentAndRotation(dc);
+        assertTrue(dc.getRotationReversionController().isAnyOverrideActive());
+        assertEquals(ROTATION_0, dc.getRotation());
+
+        app.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+        updateAllDisplayContentAndRotation(dc);
+        assertFalse(dc.getRotationReversionController().isAnyOverrideActive());
+        assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED,
+                dc.getDisplayRotation().getUserRotationMode());
+        assertEquals(ROTATION_90, dc.getDisplayRotation().getUserRotation());
+        assertEquals(ROTATION_90, dc.getDisplayRotation()
+                .rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_0));
+        dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE,
+                ROTATION_0);
+    }
+
     @Test
     public void testOnDescendantOrientationRequestChanged() {
         final DisplayContent dc = createNewDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index c2b3783..a311726 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -365,6 +365,23 @@
     }
 
     @Test
+    public void testCameraDisconnected_revertRotationAndRefresh() throws Exception {
+        configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE);
+        // Open camera and test for compat treatment
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
+        assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
+                SCREEN_ORIENTATION_LANDSCAPE);
+        assertActivityRefreshRequested(/* refreshRequested */ true);
+        // Close camera and test for revert
+        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+        callOnActivityConfigurationChanging(mActivity, /* isDisplayRotationChanging */ true);
+        assertEquals(mDisplayRotationCompatPolicy.getOrientation(),
+                SCREEN_ORIENTATION_UNSPECIFIED);
+        assertActivityRefreshRequested(/* refreshRequested */ true);
+    }
+
+    @Test
     public void testGetOrientation_cameraConnectionClosed_returnUnspecified() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 495f868..4b2d107 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -70,6 +70,7 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -114,6 +115,7 @@
 
     private static WindowManagerService sMockWm;
     private DisplayContent mMockDisplayContent;
+    private DisplayRotationReversionController mMockDisplayRotationReversionController;
     private DisplayPolicy mMockDisplayPolicy;
     private DisplayAddress mMockDisplayAddress;
     private Context mMockContext;
@@ -140,6 +142,8 @@
 
     private DeviceStateController mDeviceStateController;
     private TestDisplayRotation mTarget;
+    @Nullable
+    private DisplayRotationImmersiveAppCompatPolicy mDisplayRotationImmersiveAppCompatPolicyMock;
 
     @BeforeClass
     public static void setUpOnce() {
@@ -165,7 +169,7 @@
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
         LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
-
+        mDisplayRotationImmersiveAppCompatPolicyMock = null;
         mBuilder = new DisplayRotationBuilder();
     }
 
@@ -578,6 +582,38 @@
     }
 
     @Test
+    public void testNotifiesChoiceWhenSensorUpdates_immersiveApp() throws Exception {
+        mDisplayRotationImmersiveAppCompatPolicyMock = mock(
+                DisplayRotationImmersiveAppCompatPolicy.class);
+        when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced(
+                Surface.ROTATION_90)).thenReturn(true);
+
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+        assertTrue(waitForUiHandler());
+
+        verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+
+        // An imaginary ActivityRecord.setRequestedOrientation call disables immersive mode:
+        when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced(
+                Surface.ROTATION_90)).thenReturn(false);
+
+        // And then ActivityRecord.setRequestedOrientation calls onSetRequestedOrientation.
+        mTarget.onSetRequestedOrientation();
+
+        // onSetRequestedOrientation should lead to a second call to
+        // mOrientationListener.onProposedRotationChanged
+        // but now, instead of notifying mMockStatusBarManagerInternal, it calls updateRotation:
+        verify(sMockWm).updateRotation(false, false);
+    }
+
+    @Test
     public void testAllowAllRotations_allowsUpsideDownSuggestion()
             throws Exception {
         mBuilder.build();
@@ -1374,6 +1410,10 @@
             when(mMockContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
                     .thenReturn(mSupportHalfFoldAutoRotateOverride);
+            mMockDisplayRotationReversionController =
+                    mock(DisplayRotationReversionController.class);
+            when(mMockDisplayContent.getRotationReversionController())
+                        .thenReturn(mMockDisplayRotationReversionController);
 
             mMockResolver = mock(ContentResolver.class);
             when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
@@ -1404,7 +1444,7 @@
         }
     }
 
-    private static class TestDisplayRotation extends DisplayRotation {
+    private class TestDisplayRotation extends DisplayRotation {
         IntConsumer mProposedRotationCallback;
 
         TestDisplayRotation(DisplayContent dc, DisplayAddress address, DisplayPolicy policy,
@@ -1417,7 +1457,7 @@
         @Override
         DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
                 WindowManagerService service, DisplayContent displayContent) {
-            return null;
+            return mDisplayRotationImmersiveAppCompatPolicyMock;
         }
 
         @Override
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 e96d1ab..de943d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -428,20 +429,24 @@
                 .setLaunchedFromUid(mActivity.getUid())
                 .build();
         doReturn(false).when(translucentActivity).fillsParent();
-        WindowConfiguration translucentWinConf = translucentActivity.getWindowConfiguration();
-        translucentActivity.setActivityType(ACTIVITY_TYPE_STANDARD);
-        translucentActivity.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        translucentActivity.setDisplayWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        translucentActivity.setAlwaysOnTop(true);
+        final Configuration requestedConfig =
+                translucentActivity.getRequestedOverrideConfiguration();
+        final WindowConfiguration translucentWinConf = requestedConfig.windowConfiguration;
+        translucentWinConf.setActivityType(ACTIVITY_TYPE_STANDARD);
+        translucentWinConf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        translucentWinConf.setDisplayWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        translucentWinConf.setAlwaysOnTop(true);
+        translucentActivity.onRequestedOverrideConfigurationChanged(requestedConfig);
 
         mTask.addChild(translucentActivity);
 
-        // We check the WIndowConfiguration properties
-        translucentWinConf = translucentActivity.getWindowConfiguration();
+        // The original override of WindowConfiguration should keep.
         assertEquals(ACTIVITY_TYPE_STANDARD, translucentActivity.getActivityType());
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getWindowingMode());
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getDisplayWindowingMode());
         assertTrue(translucentWinConf.isAlwaysOnTop());
+        // Unless display is going to be rotated, it should always inherit from parent.
+        assertEquals(ROTATION_UNDEFINED, translucentWinConf.getDisplayRotation());
     }
 
     @Test