Merge "Fix one-handed mode tap app to exit doesn't work in factory boot" into sc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 86fa06c..9572808 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2603,7 +2603,7 @@
             final int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
             if (callingUid != uid && !UserHandle.isCore(callingUid)) {
                 throw new SecurityException("Uid " + callingUid
-                        + " cannot query hasScheduleExactAlarm for uid " + uid);
+                        + " cannot query hasScheduleExactAlarm for package " + packageName);
             }
             return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false;
         }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3915abe..0c64c86 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -369,11 +369,12 @@
     @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R,
             publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.")
     Configuration mConfiguration;
+    @GuardedBy("this")
+    private boolean mUpdateHttpProxyOnBind = false;
     @UnsupportedAppUsage
     Application mInitialApplication;
     @UnsupportedAppUsage
-    final ArrayList<Application> mAllApplications
-            = new ArrayList<Application>();
+    final ArrayList<Application> mAllApplications = new ArrayList<>();
     /**
      * Bookkeeping of instantiated backup agents indexed first by user id, then by package name.
      * Indexing by user id supports parallel backups across users on system packages as they run in
@@ -1187,8 +1188,18 @@
         }
 
         public void updateHttpProxy() {
-            ActivityThread.updateHttpProxy(
-                    getApplication() != null ? getApplication() : getSystemContext());
+            final Application app;
+            synchronized (ActivityThread.this) {
+                app = getApplication();
+                if (null == app) {
+                    // The app is not bound yet. Make a note to update the HTTP proxy when the
+                    // app is bound.
+                    mUpdateHttpProxyOnBind = true;
+                    return;
+                }
+            }
+            // App is present, update the proxy inline.
+            ActivityThread.updateHttpProxy(app);
         }
 
         public void processInBackground() {
@@ -5643,8 +5654,8 @@
      */
     private void scheduleRelaunchActivityIfPossible(@NonNull ActivityClientRecord r,
             boolean preserveWindow) {
-        if (r.activity.mFinished || r.token instanceof Binder) {
-            // Do not schedule relaunch if the activity is finishing or not a local object (e.g.
+        if ((r.activity != null && r.activity.mFinished) || r.token instanceof Binder) {
+            // Do not schedule relaunch if the activity is finishing or is a local object (e.g.
             // created by ActivtiyGroup that server side doesn't recognize it).
             return;
         }
@@ -6685,6 +6696,15 @@
             sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);
 
             mInitialApplication = app;
+            final boolean updateHttpProxy;
+            synchronized (this) {
+                updateHttpProxy = mUpdateHttpProxyOnBind;
+                // This synchronized block ensures that any subsequent call to updateHttpProxy()
+                // will see a non-null mInitialApplication.
+            }
+            if (updateHttpProxy) {
+                ActivityThread.updateHttpProxy(app);
+            }
 
             // don't bring up providers in restricted mode; they may depend on the
             // app's custom Application class
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4ddb546..d932a29 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2849,14 +2849,14 @@
     private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
 
     /**
-     * If a thread is currently executing a two-way binder transaction, this stores the
-     * ops that were noted blaming any app (the caller, the caller of the caller, etc).
+     * If a thread is currently executing a two-way binder transaction, this stores the op-codes of
+     * the app-ops that were noted during this transaction.
      *
      * @see #getNotedOpCollectionMode
      * @see #collectNotedOpSync
      */
-    private static final ThreadLocal<ArrayMap<String, ArrayMap<String, long[]>>>
-            sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>();
+    private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
+            new ThreadLocal<>();
 
     /** Whether noting for an appop should be collected */
     private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
@@ -7205,6 +7205,34 @@
      * @hide
      */
     public interface OnOpStartedListener {
+
+        /**
+         * Represents a start operation that was unsuccessful
+         * @hide
+         */
+        public int START_TYPE_FAILED = 0;
+
+        /**
+         * Represents a successful start operation
+         * @hide
+         */
+        public int START_TYPE_STARTED = 1;
+
+        /**
+         * Represents an operation where a restricted operation became unrestricted, and resumed.
+         * @hide
+         */
+        public int START_TYPE_RESUMED = 2;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            START_TYPE_FAILED,
+            START_TYPE_STARTED,
+            START_TYPE_RESUMED
+        })
+        public @interface StartedType {}
+
         /**
          * Called when an op was started.
          *
@@ -7213,11 +7241,35 @@
          * @param uid The UID performing the operation.
          * @param packageName The package performing the operation.
          * @param attributionTag The attribution tag performing the operation.
-         * @param flags The flags of this op
+         * @param flags The flags of this op.
          * @param result The result of the start.
          */
         void onOpStarted(int op, int uid, String packageName, String attributionTag,
                 @OpFlags int flags, @Mode int result);
+
+        /**
+         * Called when an op was started.
+         *
+         * Note: This is only for op starts. It is not called when an op is noted or stopped.
+         * By default, unless this method is overridden, no code will be executed for resume
+         * events.
+         * @param op The op code.
+         * @param uid The UID performing the operation.
+         * @param packageName The package performing the operation.
+         * @param attributionTag The attribution tag performing the operation.
+         * @param flags The flags of this op.
+         * @param result The result of the start.
+         * @param startType The start type of this start event. Either failed, resumed, or started.
+         * @param attributionFlags The location of this started op in an attribution chain.
+         * @param attributionChainId The ID of the attribution chain of this op, if it is in one.
+         */
+        default void onOpStarted(int op, int uid, String packageName, String attributionTag,
+                @OpFlags int flags, @Mode int result, @StartedType int startType,
+                @AttributionFlags int attributionFlags, int attributionChainId) {
+            if (startType != START_TYPE_RESUMED) {
+                onOpStarted(op, uid, packageName, attributionTag, flags, result);
+            }
+        }
     }
 
     AppOpsManager(Context context, IAppOpsService service) {
@@ -7858,8 +7910,10 @@
              cb = new IAppOpsStartedCallback.Stub() {
                  @Override
                  public void opStarted(int op, int uid, String packageName, String attributionTag,
-                         int flags, int mode) {
-                     callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode);
+                         int flags, int mode, int startType, int attributionFlags,
+                         int attributionChainId) {
+                     callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode,
+                             startType, attributionFlags, attributionChainId);
                  }
              };
              mStartedWatchers.put(callback, cb);
@@ -9052,6 +9106,66 @@
     }
 
     /**
+     * State of a temporarily paused noted app-ops collection.
+     *
+     * @see #pauseNotedAppOpsCollection()
+     *
+     * @hide
+     */
+    public static class PausedNotedAppOpsCollection {
+        final int mUid;
+        final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps;
+
+        PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String,
+                long[]> collectedNotedAppOps) {
+            mUid = uid;
+            mCollectedNotedAppOps = collectedNotedAppOps;
+        }
+    }
+
+    /**
+     * Temporarily suspend collection of noted app-ops when binder-thread calls into the other
+     * process. During such a call there might be call-backs coming back on the same thread which
+     * should not be accounted to the current collection.
+     *
+     * @return a state needed to resume the collection
+     *
+     * @hide
+     */
+    public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
+        Integer previousUid = sBinderThreadCallingUid.get();
+        if (previousUid != null) {
+            ArrayMap<String, long[]> previousCollectedNotedAppOps =
+                    sAppOpsNotedInThisBinderTransaction.get();
+
+            sBinderThreadCallingUid.remove();
+            sAppOpsNotedInThisBinderTransaction.remove();
+
+            return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps);
+        }
+
+        return null;
+    }
+
+    /**
+     * Resume a collection paused via {@link #pauseNotedAppOpsCollection}.
+     *
+     * @param prevCollection The state of the previous collection
+     *
+     * @hide
+     */
+    public static void resumeNotedAppOpsCollection(
+            @Nullable PausedNotedAppOpsCollection prevCollection) {
+        if (prevCollection != null) {
+            sBinderThreadCallingUid.set(prevCollection.mUid);
+
+            if (prevCollection.mCollectedNotedAppOps != null) {
+                sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps);
+            }
+        }
+    }
+
+    /**
      * Finish collection of noted appops on this thread.
      *
      * <p>Called at the end of a two way binder transaction.
@@ -9091,47 +9205,26 @@
      */
     @TestApi
     public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
-        collectNotedOpSync(sOpStrToOp.get(syncOp.getOp()), syncOp.getAttributionTag(),
-                syncOp.getPackageName());
-    }
-
-    /**
-     * Collect a noted op when inside of a two-way binder call.
-     *
-     * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
-     *
-     * @param code the op code to note for
-     * @param attributionTag the attribution tag to note for
-     * @param packageName the package to note for
-     */
-    private static void collectNotedOpSync(int code, @Nullable String attributionTag,
-            @NonNull String packageName) {
         // If this is inside of a two-way binder call:
         // We are inside of a two-way binder call. Delivered to caller via
         // {@link #prefixParcelWithAppOpsIfNeeded}
-        ArrayMap<String, ArrayMap<String, long[]>> appOpsNoted =
-                sAppOpsNotedInThisBinderTransaction.get();
+        int op = sOpStrToOp.get(syncOp.getOp());
+        ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
         if (appOpsNoted == null) {
             appOpsNoted = new ArrayMap<>(1);
             sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
         }
 
-        ArrayMap<String, long[]> packageAppOpsNotedForAttribution = appOpsNoted.get(packageName);
-        if (packageAppOpsNotedForAttribution == null) {
-            packageAppOpsNotedForAttribution = new ArrayMap<>(1);
-            appOpsNoted.put(packageName, packageAppOpsNotedForAttribution);
-        }
-
-        long[] appOpsNotedForAttribution = packageAppOpsNotedForAttribution.get(attributionTag);
+        long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag());
         if (appOpsNotedForAttribution == null) {
             appOpsNotedForAttribution = new long[2];
-            packageAppOpsNotedForAttribution.put(attributionTag, appOpsNotedForAttribution);
+            appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution);
         }
 
-        if (code < 64) {
-            appOpsNotedForAttribution[0] |= 1L << code;
+        if (op < 64) {
+            appOpsNotedForAttribution[0] |= 1L << op;
         } else {
-            appOpsNotedForAttribution[1] |= 1L << (code - 64);
+            appOpsNotedForAttribution[1] |= 1L << (op - 64);
         }
     }
 
@@ -9185,7 +9278,9 @@
             }
         }
 
-        if (isListeningForOpNotedInBinderTransaction()) {
+        Integer binderUid = sBinderThreadCallingUid.get();
+
+        if (binderUid != null && binderUid == uid) {
             return COLLECT_SYNC;
         } else {
             return COLLECT_ASYNC;
@@ -9204,32 +9299,20 @@
      */
     // TODO (b/186872903) Refactor how sync noted ops are propagated.
     public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
-        if (!isListeningForOpNotedInBinderTransaction()) {
-            return;
-        }
-        final ArrayMap<String, ArrayMap<String, long[]>> notedAppOps =
-                sAppOpsNotedInThisBinderTransaction.get();
+        ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
         if (notedAppOps == null) {
             return;
         }
 
         p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
 
-        final int packageCount = notedAppOps.size();
-        p.writeInt(packageCount);
+        int numAttributionWithNotesAppOps = notedAppOps.size();
+        p.writeInt(numAttributionWithNotesAppOps);
 
-        for (int i = 0; i < packageCount; i++) {
+        for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
             p.writeString(notedAppOps.keyAt(i));
-
-            final ArrayMap<String, long[]> notedTagAppOps = notedAppOps.valueAt(i);
-            final int tagCount = notedTagAppOps.size();
-            p.writeInt(tagCount);
-
-            for (int j = 0; j < tagCount; j++) {
-                p.writeString(notedTagAppOps.keyAt(j));
-                p.writeLong(notedTagAppOps.valueAt(j)[0]);
-                p.writeLong(notedTagAppOps.valueAt(j)[1]);
-            }
+            p.writeLong(notedAppOps.valueAt(i)[0]);
+            p.writeLong(notedAppOps.valueAt(i)[1]);
         }
     }
 
@@ -9244,54 +9327,36 @@
      * @hide
      */
     public static void readAndLogNotedAppops(@NonNull Parcel p) {
-        final int packageCount = p.readInt();
-        if (packageCount <= 0) {
-            return;
-        }
+        int numAttributionsWithNotedAppOps = p.readInt();
 
-        final String myPackageName = ActivityThread.currentPackageName();
+        for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
+            String attributionTag = p.readString();
+            long[] rawNotedAppOps = new long[2];
+            rawNotedAppOps[0] = p.readLong();
+            rawNotedAppOps[1] = p.readLong();
 
-        synchronized (sLock) {
-            for (int i = 0; i < packageCount; i++) {
-                final String packageName = p.readString();
+            if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
+                BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
 
-                final int tagCount = p.readInt();
-                for (int j = 0; j < tagCount; j++) {
-                    final String attributionTag = p.readString();
-                    final long[] rawNotedAppOps = new long[2];
-                    rawNotedAppOps[0] = p.readLong();
-                    rawNotedAppOps[1] = p.readLong();
-
-                    if (rawNotedAppOps[0] == 0 && rawNotedAppOps[1] == 0) {
-                        continue;
-                    }
-
-                    final BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
+                synchronized (sLock) {
                     for (int code = notedAppOps.nextSetBit(0); code != -1;
                             code = notedAppOps.nextSetBit(code + 1)) {
-                        if (Objects.equals(myPackageName, packageName)) {
-                            if (sOnOpNotedCallback != null) {
-                                sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code,
-                                        attributionTag, packageName));
-                            } else {
-                                String message = getFormattedStackTrace();
-                                sUnforwardedOps.add(new AsyncNotedAppOp(code, Process.myUid(),
-                                        attributionTag, message, System.currentTimeMillis()));
-                                if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
-                                    sUnforwardedOps.remove(0);
-                                }
+                        if (sOnOpNotedCallback != null) {
+                            sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
+                        } else {
+                            String message = getFormattedStackTrace();
+                            sUnforwardedOps.add(
+                                    new AsyncNotedAppOp(code, Process.myUid(), attributionTag,
+                                            message, System.currentTimeMillis()));
+                            if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+                                sUnforwardedOps.remove(0);
                             }
-                        } else if (isListeningForOpNotedInBinderTransaction()) {
-                            collectNotedOpSync(code, attributionTag, packageName);
                         }
                     }
-                    for (int code = notedAppOps.nextSetBit(0); code != -1;
-                            code = notedAppOps.nextSetBit(code + 1)) {
-                        if (Objects.equals(myPackageName, packageName)) {
-                            sMessageCollector.onNoted(new SyncNotedAppOp(code,
-                                    attributionTag, packageName));
-                        }
-                    }
+                }
+                for (int code = notedAppOps.nextSetBit(0); code != -1;
+                        code = notedAppOps.nextSetBit(code + 1)) {
+                    sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
                 }
             }
         }
@@ -9398,15 +9463,7 @@
      * @hide
      */
     public static boolean isListeningForOpNoted() {
-        return sOnOpNotedCallback != null || isListeningForOpNotedInBinderTransaction()
-                || isCollectingStackTraces();
-    }
-
-    /**
-     * @return whether we are in a binder transaction and collecting appops.
-     */
-    private static boolean isListeningForOpNotedInBinderTransaction() {
-        return sBinderThreadCallingUid.get() != null;
+        return sOnOpNotedCallback != null || isCollectingStackTraces();
     }
 
     /**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9d149cf..4b054f4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6122,28 +6122,29 @@
                 // change the background bgColor
                 CharSequence title = action.title;
                 ColorStateList[] outResultColor = new ColorStateList[1];
-                int background = getColors(p).getSecondaryAccentColor();
+                int buttonFillColor = getColors(p).getSecondaryAccentColor();
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
-                    title = ensureColorSpanContrast(title, background, outResultColor);
+                    int notifBackgroundColor = getColors(p).getBackgroundColor();
+                    title = ensureColorSpanContrast(title, notifBackgroundColor, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
                 boolean hasColorOverride = outResultColor[0] != null;
                 if (hasColorOverride) {
                     // There's a span spanning the full text, let's take it and use it as the
                     // background color
-                    background = outResultColor[0].getDefaultColor();
+                    buttonFillColor = outResultColor[0].getDefaultColor();
                 }
                 final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
-                        background, mInNightMode);
+                        buttonFillColor, mInNightMode);
                 button.setTextColor(R.id.action0, textColor);
                 // We only want about 20% alpha for the ripple
                 final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
                 button.setColorStateList(R.id.action0, "setRippleColor",
                         ColorStateList.valueOf(rippleColor));
                 button.setColorStateList(R.id.action0, "setButtonBackground",
-                        ColorStateList.valueOf(background));
+                        ColorStateList.valueOf(buttonFillColor));
                 if (p.mCallStyleActions) {
                     button.setImageViewIcon(R.id.action0, action.getIcon());
                     boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
@@ -6176,8 +6177,8 @@
          *                    there exists a full length color span.
          * @return the contrasted charSequence
          */
-        private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
-                ColorStateList[] outResultColor) {
+        private static CharSequence ensureColorSpanContrast(CharSequence charSequence,
+                int background, ColorStateList[] outResultColor) {
             if (charSequence instanceof Spanned) {
                 Spanned ss = (Spanned) charSequence;
                 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
@@ -6197,8 +6198,9 @@
                             int[] colors = textColor.getColors();
                             int[] newColors = new int[colors.length];
                             for (int i = 0; i < newColors.length; i++) {
+                                boolean isBgDark = isColorDark(background);
                                 newColors[i] = ContrastColorUtil.ensureLargeTextContrast(
-                                        colors[i], background, mInNightMode);
+                                        colors[i], background, isBgDark);
                             }
                             textColor = new ColorStateList(textColor.getStates().clone(),
                                     newColors);
@@ -6217,8 +6219,9 @@
                     } else if (resultSpan instanceof ForegroundColorSpan) {
                         ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
                         int foregroundColor = originalSpan.getForegroundColor();
+                        boolean isBgDark = isColorDark(background);
                         foregroundColor = ContrastColorUtil.ensureLargeTextContrast(
-                                foregroundColor, background, mInNightMode);
+                                foregroundColor, background, isBgDark);
                         if (fullLength) {
                             outResultColor[0] = ColorStateList.valueOf(foregroundColor);
                             resultSpan = null;
@@ -6238,6 +6241,19 @@
         }
 
         /**
+         * Determines if the color is light or dark.  Specifically, this is using the same metric as
+         * {@link ContrastColorUtil#resolvePrimaryColor(Context, int, boolean)} and peers so that
+         * the direction of color shift is consistent.
+         *
+         * @param color the color to check
+         * @return true if the color has higher contrast with white than black
+         */
+        private static boolean isColorDark(int color) {
+            // as per ContrastColorUtil.shouldUseDark, this uses the color contrast midpoint.
+            return ContrastColorUtil.calculateLuminance(color) <= 0.17912878474;
+        }
+
+        /**
          * @return Whether we are currently building a notification from a legacy (an app that
          *         doesn't create material notifications by itself) app.
          */
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 32d889e..7c0c08a7f 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcelable;
-import android.os.Process;
 
 import com.android.internal.annotations.Immutable;
 import com.android.internal.util.DataClass;
@@ -29,6 +28,8 @@
 /**
  * Description of an app-op that was noted for the current process.
  *
+ * Note: package name is currently unused in the system.
+ *
  * <p>This is either delivered after a
  * {@link AppOpsManager.OnOpNotedCallback#onNoted(SyncNotedAppOp) two way binder call} or
  * when the app
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 65cdca9..1dd32fe 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -1025,6 +1025,10 @@
     public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
             int value) {
         if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
+        if (value < 0) {
+            Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
+            return false;
+        }
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index cb3bf29..8ff0181 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -168,6 +168,15 @@
                 dest.writeByteArray(mManufacturerDataMask);
             }
         }
+
+        // IRK
+        if (mDeviceAddress != null) {
+            dest.writeInt(mAddressType);
+            dest.writeInt(mIrk == null ? 0 : 1);
+            if (mIrk != null) {
+                dest.writeByteArray(mIrk);
+            }
+        }
     }
 
     /**
@@ -187,8 +196,10 @@
             if (in.readInt() == 1) {
                 builder.setDeviceName(in.readString());
             }
+            String address = null;
+            // If we have a non-null address
             if (in.readInt() == 1) {
-                builder.setDeviceAddress(in.readString());
+                address = in.readString();
             }
             if (in.readInt() == 1) {
                 ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
@@ -245,6 +256,17 @@
                 }
             }
 
+            // IRK
+            if (address != null) {
+                final int addressType = in.readInt();
+                if (in.readInt() == 1) {
+                    final byte[] irk = new byte[16];
+                    in.readByteArray(irk);
+                    builder.setDeviceAddress(address, addressType, irk);
+                } else {
+                    builder.setDeviceAddress(address, addressType);
+                }
+            }
             return builder.build();
         }
     };
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a741f96..c714f507 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -745,9 +745,6 @@
         if (Binder.getCallingPid() == Process.myPid()) {
             return PermissionChecker.PERMISSION_GRANTED;
         }
-        if (!attributionSource.checkCallingUid()) {
-            return PermissionChecker.PERMISSION_HARD_DENIED;
-        }
         return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(getContext(),
                 permission, -1, new AttributionSource(getContext().getAttributionSource(),
                         attributionSource), /*message*/ null);
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 4526ab77..fa7ce11 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -24,12 +24,12 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.service.SensorPrivacyIndividualEnabledSensorProto;
 import android.service.SensorPrivacyToggleSourceProto;
 import android.util.ArrayMap;
@@ -379,7 +379,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
-        return isSensorPrivacyEnabled(sensor, getCurrentUserId());
+        return isSensorPrivacyEnabled(sensor, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -410,7 +410,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
     public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor,
             boolean enable) {
-        setSensorPrivacy(source, sensor, enable, getCurrentUserId());
+        setSensorPrivacy(source, sensor, enable, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -446,7 +446,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
     public void setSensorPrivacyForProfileGroup(@Sources.Source int source,
             @Sensors.Sensor int sensor, boolean enable) {
-        setSensorPrivacyForProfileGroup(source , sensor, enable, getCurrentUserId());
+        setSensorPrivacyForProfileGroup(source , sensor, enable, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -481,7 +481,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
     public void suppressSensorPrivacyReminders(int sensor,
             boolean suppress) {
-        suppressSensorPrivacyReminders(sensor, suppress, getCurrentUserId());
+        suppressSensorPrivacyReminders(sensor, suppress, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -609,12 +609,4 @@
         }
     }
 
-    private int getCurrentUserId() {
-        try {
-            return ActivityManager.getService().getCurrentUserId();
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        return 0;
-    }
 }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c78dd53..b55a1cb 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2318,6 +2318,51 @@
             new Key<Float>("android.control.zoomRatio", float.class);
 
     /**
+     * <p>Framework-only private key which informs camera fwk that the AF regions has been set
+     * by the client and those regions need not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> CONTROL_AF_REGIONS_SET =
+            new Key<Boolean>("android.control.afRegionsSet", boolean.class);
+
+    /**
+     * <p>Framework-only private key which informs camera fwk that the AE regions has been set
+     * by the client and those regions need not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_REGIONS
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> CONTROL_AE_REGIONS_SET =
+            new Key<Boolean>("android.control.aeRegionsSet", boolean.class);
+
+    /**
+     * <p>Framework-only private key which informs camera fwk that the AF regions has been set
+     * by the client and those regions need not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> CONTROL_AWB_REGIONS_SET =
+            new Key<Boolean>("android.control.awbRegionsSet", boolean.class);
+
+    /**
      * <p>Operation mode for edge
      * enhancement.</p>
      * <p>Edge enhancement improves sharpness and details in the captured image. OFF means
@@ -3057,6 +3102,21 @@
             new Key<Integer>("android.scaler.rotateAndCrop", int.class);
 
     /**
+     * <p>Framework-only private key which informs camera fwk that the scaler crop region
+     * ({@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}) has been set by the client and it need
+     * not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SCALER_CROP_REGION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> SCALER_CROP_REGION_SET =
+            new Key<Boolean>("android.scaler.cropRegionSet", boolean.class);
+
+    /**
      * <p>Duration each pixel is exposed to
      * light.</p>
      * <p>If the sensor can't expose this exact duration, it will shorten the
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
index 3af1b5b..0dbf29d 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -91,7 +91,7 @@
      * </p>
      * <p>
      * The {@code maxImages} parameter determines the maximum number of
-     * {@link Image} objects that can be be acquired from each of the {@code ImageReader}
+     * {@link Image} objects that can be acquired from each of the {@code ImageReader}
      * within the {@code MultiResolutionImageReader}. However, requesting more buffers will
      * use up more memory, so it is important to use only the minimum number necessary. The
      * application is strongly recommended to acquire no more than {@code maxImages} images
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index bfc1f27..d5a35bc 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -446,10 +446,16 @@
         }
     }
 
+    @Override
+    protected void finalize() throws Throwable {
+        if (mHandlerThread != null) {
+            mHandlerThread.quitSafely();
+        }
+        super.finalize();
+    }
+
     public void release() {
         synchronized (mInterfaceLock) {
-            mHandlerThread.quitSafely();
-
             if (mSessionProcessor != null) {
                 try {
                     mSessionProcessor.deInitSession();
@@ -812,6 +818,8 @@
                     Log.e(TAG,"Failed to parcel buffer fence!");
                 }
             }
+            parcelImage.width = img.getWidth();
+            parcelImage.height = img.getHeight();
             parcelImage.format = img.getFormat();
             parcelImage.timestamp = img.getTimestamp();
             parcelImage.transform = img.getTransform();
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 11b137ca..0bf812e 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -696,6 +696,16 @@
                 mCurrentSession.replaceSessionClose();
             }
 
+            if (mCurrentExtensionSession != null) {
+                mCurrentExtensionSession.release();
+                mCurrentExtensionSession = null;
+            }
+
+            if (mCurrentAdvancedExtensionSession != null) {
+                mCurrentAdvancedExtensionSession.release();
+                mCurrentAdvancedExtensionSession = null;
+            }
+
             // TODO: dont block for this
             boolean configureSuccess = true;
             CameraAccessException pendingException = null;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 537b894..7d29a7d 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -49,8 +49,6 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.IInterface;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.annotation.NonNull;
@@ -265,13 +263,6 @@
             } catch (ClassCastException e) {
                 throw new UnsupportedOperationException("Failed casting preview processor!");
             }
-            if (mClientRepeatingRequestSurface != null) {
-                mPreviewRequestUpdateProcessor.onOutputSurface(mClientRepeatingRequestSurface,
-                        nativeGetSurfaceFormat(mClientRepeatingRequestSurface));
-                mRepeatingRequestImageWriter = ImageWriter.newInstance(
-                        mClientRepeatingRequestSurface, PREVIEW_QUEUE_SIZE,
-                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
-            }
             mRepeatingRequestImageReader = ImageReader.newInstance(repeatingSurfaceInfo.mWidth,
                     repeatingSurfaceInfo.mHeight,
                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT,
@@ -285,11 +276,6 @@
             mPreviewRequestUpdateProcessor.onImageFormatUpdate(
                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
         } else {
-            if (mClientRepeatingRequestSurface != null) {
-                mRepeatingRequestImageWriter = ImageWriter.newInstance(
-                        mClientRepeatingRequestSurface, PREVIEW_QUEUE_SIZE,
-                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
-            }
             mRepeatingRequestImageReader = ImageReader.newInstance(repeatingSurfaceInfo.mWidth,
                     repeatingSurfaceInfo.mHeight,
                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT,
@@ -320,7 +306,6 @@
                 mBurstCaptureImageReader = ImageReader.newInstance(surfaceInfo.mWidth,
                         surfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
                         mImageExtender.getMaxCaptureStage());
-                mImageProcessor.onOutputSurface(mClientCaptureSurface, surfaceInfo.mFormat);
             } else {
                 // The client doesn't intend to trigger multi-frame capture, however the
                 // image extender still needs to get initialized and the camera still capture
@@ -367,6 +352,29 @@
         }
     }
 
+    private void finishPipelineInitialization() throws RemoteException {
+        if (mClientRepeatingRequestSurface != null) {
+            if (mPreviewProcessorType == IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
+                mPreviewRequestUpdateProcessor.onOutputSurface(mClientRepeatingRequestSurface,
+                        nativeGetSurfaceFormat(mClientRepeatingRequestSurface));
+                mRepeatingRequestImageWriter = ImageWriter.newInstance(
+                        mClientRepeatingRequestSurface,
+                        PREVIEW_QUEUE_SIZE,
+                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
+            } else if (mPreviewProcessorType == IPreviewExtenderImpl.PROCESSOR_TYPE_NONE) {
+                mRepeatingRequestImageWriter = ImageWriter.newInstance(
+                        mClientRepeatingRequestSurface,
+                        PREVIEW_QUEUE_SIZE,
+                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
+            }
+        }
+        if ((mImageProcessor != null) && (mClientCaptureSurface != null)) {
+            CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
+                    mClientCaptureSurface);
+            mImageProcessor.onOutputSurface(mClientCaptureSurface, surfaceInfo.mFormat);
+        }
+    }
+
     /**
      * @hide
      */
@@ -622,11 +630,18 @@
                 new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
     }
 
+    @Override
+    protected void finalize() throws Throwable {
+        if (mHandlerThread != null) {
+            mHandlerThread.quitSafely();
+        }
+        super.finalize();
+    }
+
     /** @hide */
     public void release() {
         synchronized (mInterfaceLock) {
             mInternalRepeatingRequestEnabled = false;
-            mHandlerThread.quitSafely();
 
             try {
                 mPreviewExtender.onDeInit();
@@ -750,6 +765,7 @@
             synchronized (mInterfaceLock) {
                 mCaptureSession = session;
                 try {
+                    finishPipelineInitialization();
                     CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to initialize session! Extension service does"
@@ -1640,6 +1656,8 @@
                 Log.e(TAG,"Failed to parcel buffer fence!");
             }
         }
+        parcelImage.width = img.getWidth();
+        parcelImage.height = img.getHeight();
         parcelImage.format = img.getFormat();
         parcelImage.timestamp = img.getTimestamp();
         parcelImage.transform = img.getTransform();
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
index 950d716b..afefcbe 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.graphics.ImageFormat;
 import android.graphics.PixelFormat;
-import android.hardware.HardwareBuffer;
 import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
@@ -76,43 +75,22 @@
         ImageWriter writer = null;
         Image img = null;
         SurfaceInfo surfaceInfo = new SurfaceInfo();
-        int nativeFormat = SurfaceUtils.getSurfaceFormat(s);
+        int nativeFormat = SurfaceUtils.detectSurfaceFormat(s);
         int dataspace = SurfaceUtils.getSurfaceDataspace(s);
+        Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+        surfaceInfo.mFormat = nativeFormat;
+        surfaceInfo.mWidth = surfaceSize.getWidth();
+        surfaceInfo.mHeight = surfaceSize.getHeight();
+        surfaceInfo.mUsage = SurfaceUtils.getSurfaceUsage(s);
         // Jpeg surfaces cannot be queried for their usage and other parameters
         // in the usual way below. A buffer can only be de-queued after the
         // producer overrides the surface dimensions to (width*height) x 1.
         if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
                 (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
             surfaceInfo.mFormat = ImageFormat.JPEG;
-            Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
-            surfaceInfo.mWidth = surfaceSize.getWidth();
-            surfaceInfo.mHeight = surfaceSize.getHeight();
             return surfaceInfo;
         }
 
-        HardwareBuffer buffer = null;
-        try {
-            writer = ImageWriter.newInstance(s, 1);
-            img = writer.dequeueInputImage();
-            buffer = img.getHardwareBuffer();
-            surfaceInfo.mFormat = buffer.getFormat();
-            surfaceInfo.mWidth = buffer.getWidth();
-            surfaceInfo.mHeight = buffer.getHeight();
-            surfaceInfo.mUsage = buffer.getUsage();
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to query surface, returning defaults!");
-        } finally {
-            if (buffer != null) {
-                buffer.close();
-            }
-            if (img != null) {
-                img.close();
-            }
-            if (writer != null) {
-                writer.close();
-            }
-        }
-
         return surfaceInfo;
     }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 6cbe107..196134b 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -53,6 +53,7 @@
 import android.hardware.camera2.params.Face;
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.MandatoryStreamCombination;
 import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
 import android.hardware.camera2.params.OisSample;
@@ -1708,6 +1709,34 @@
                 metadata.setGpsLocation((Location) value);
             }
         });
+        sSetCommandMap.put(CaptureRequest.SCALER_CROP_REGION.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setScalerCropRegion((Rect) value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AWB_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAWBRegions(value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AF_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAFRegions(value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AE_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAERegions(value);
+            }
+        });
     }
 
     private boolean setAvailableFormats(int[] value) {
@@ -1777,6 +1806,45 @@
         return true;
     }
 
+    private <T> boolean setScalerCropRegion(Rect cropRegion) {
+        if (cropRegion == null) {
+            return false;
+        }
+        setBase(CaptureRequest.SCALER_CROP_REGION_SET, true);
+        setBase(CaptureRequest.SCALER_CROP_REGION, cropRegion);
+        return true;
+    }
+
+    private <T> boolean setAFRegions(T afRegions) {
+        if (afRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AF_REGIONS_SET, true);
+        // The cast to CaptureRequest.Key is needed since java does not support template
+        // specialization and we need to route this method to
+        // setBase(CaptureRequest.Key<T> key, T value)
+        setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AF_REGIONS, afRegions);
+        return true;
+    }
+
+    private <T> boolean setAERegions(T aeRegions) {
+        if (aeRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AE_REGIONS_SET, true);
+        setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
+        return true;
+    }
+
+    private <T> boolean setAWBRegions(T awbRegions) {
+        if (awbRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AWB_REGIONS_SET, true);
+        setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
+        return true;
+    }
+
     private void updateNativeAllocation() {
         long currentBufferSize = nativeGetBufferSize(mMetadataPtr);
 
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index 8dfc0a7b..70c85a1f 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -42,7 +42,7 @@
     private final boolean mIsMultiResolution;
 
     /**
-     * Create an input configration with the width, height, and user-defined format.
+     * Create an input configuration with the width, height, and user-defined format.
      *
      * <p>Images of a user-defined format are accessible by applications. Use
      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
@@ -64,7 +64,7 @@
     }
 
     /**
-     * Create an input configration with the format and a list of multi-resolution input stream
+     * Create an input configuration with the format and a list of multi-resolution input stream
      * info.
      *
      * <p>Use {@link
@@ -108,7 +108,7 @@
     }
 
     /**
-     * Get the width of this input configration.
+     * Get the width of this input configuration.
      *
      * @return width of this input configuration.
      */
@@ -117,7 +117,7 @@
     }
 
     /**
-     * Get the height of this input configration.
+     * Get the height of this input configuration.
      *
      * @return height of this input configuration.
      */
@@ -126,7 +126,7 @@
     }
 
     /**
-     * Get the format of this input configration.
+     * Get the format of this input configuration.
      *
      * @return format of this input configuration.
      */
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index 35b5c15..fd1a331 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -105,6 +105,20 @@
     }
 
     /**
+     * Get the surface usage bits.
+     *
+     * @param surface The surface to be queried for usage.
+     * @return the native object id of the surface, 0 if surface is not backed by a native object.
+     */
+    public static long getSurfaceUsage(Surface surface) {
+        checkNotNull(surface);
+        try {
+            return nativeDetectSurfaceUsageFlags(surface);
+        } catch (IllegalArgumentException e) {
+            return 0;
+        }
+    }
+    /**
      * Get the Surface size.
      *
      * @param surface The surface to be queried for size.
@@ -146,6 +160,23 @@
     }
 
     /**
+     * Detect and retrieve the Surface format without any
+     * additional overrides.
+     *
+     * @param surface The surface to be queried for format.
+     * @return format of the surface.
+     *
+     * @throws IllegalArgumentException if the surface is already abandoned.
+     */
+    public static int detectSurfaceFormat(Surface surface) {
+        checkNotNull(surface);
+        int surfaceType = nativeDetectSurfaceType(surface);
+        if (surfaceType == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
+
+        return surfaceType;
+    }
+
+    /**
      * Get the Surface dataspace.
      *
      * @param surface The surface to be queried for dataspace.
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index d44b016..3d466a0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -560,6 +560,9 @@
             }
         }
 
+        final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
+                AppOpsManager.pauseNotedAppOpsCollection();
+
         if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
             flags |= FLAG_COLLECT_NOTED_APP_OPS;
         }
@@ -567,6 +570,8 @@
         try {
             return transactNative(code, data, reply, flags);
         } finally {
+            AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
+
             if (transactListener != null) {
                 transactListener.onTransactEnded(session);
             }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index f3e0ce9..1bc6495 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1460,15 +1460,15 @@
     /** {@hide} */
     @VisibleForTesting
     public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) {
-        try {
-            Context context = AppGlobals.getInitialApplication();
-            if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
-                // Never convert modern fd for MediaProvider, because this requires
-                // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
-                return null;
-            }
-            return MediaStore.getOriginalMediaFormatFileDescriptor(context,
-                    ParcelFileDescriptor.dup(fd));
+        Context context = AppGlobals.getInitialApplication();
+        if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
+            // Never convert modern fd for MediaProvider, because this requires
+            // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
+            return null;
+        }
+
+        try (ParcelFileDescriptor dupFd = ParcelFileDescriptor.dup(fd)) {
+            return MediaStore.getOriginalMediaFormatFileDescriptor(context, dupFd);
         } catch (Exception e) {
             Log.d(TAG, "Failed to convert to modern format file descriptor", e);
             return null;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6bca336..9f37c48 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -34,12 +34,9 @@
 
 import libcore.io.IoUtils;
 
-import java.io.BufferedReader;
 import java.io.FileDescriptor;
-import java.io.FileReader;
 import java.io.IOException;
 import java.util.Map;
-import java.util.StringTokenizer;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -1472,43 +1469,4 @@
     }
 
     private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
-
-    /**
-     * Checks if a process corresponding to a specific pid owns any file locks.
-     * @param pid The process ID for which we want to know the existence of file locks.
-     * @return true If the process holds any file locks, false otherwise.
-     * @throws IOException if /proc/locks can't be accessed.
-     *
-     * @hide
-     */
-    public static boolean hasFileLocks(int pid) throws Exception {
-        BufferedReader br = null;
-
-        try {
-            br = new BufferedReader(new FileReader("/proc/locks"));
-            String line;
-
-            while ((line = br.readLine()) != null) {
-                StringTokenizer st = new StringTokenizer(line);
-
-                for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
-                    String str = st.nextToken();
-                    try {
-                        if (i == 4 && Integer.parseInt(str) == pid) {
-                            return true;
-                        }
-                    } catch (NumberFormatException nfe) {
-                        throw new Exception("Exception parsing /proc/locks at \" "
-                                + line +  " \", token #" + i);
-                    }
-                }
-            }
-
-            return false;
-        } finally {
-            if (br != null) {
-                br.close();
-            }
-        }
-    }
 }
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 03f94c5..19f204b 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -31,7 +31,9 @@
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA;
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
 import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
@@ -63,7 +65,8 @@
  *
  * @hide
  */
-public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener {
+public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
+        AppOpsManager.OnOpStartedListener {
 
     /** Whether to show the mic and camera icons.  */
     private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
@@ -160,9 +163,10 @@
         mUserContexts = new ArrayMap<>();
         mUserContexts.put(Process.myUserHandle(), mContext);
         // TODO ntmyren: make this listen for flag enable/disable changes
-        String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
-        mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops,
-                context.getMainExecutor(), this);
+        String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
+        mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this);
+        int[] ops = { OP_CAMERA, OP_RECORD_AUDIO };
+        mAppOpsManager.startWatchingStarted(ops, this);
     }
 
     private Context getUserContext(UserHandle user) {
@@ -182,25 +186,65 @@
     public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
             @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags,
             int attributionChainId) {
-        if (attributionChainId == ATTRIBUTION_CHAIN_ID_NONE
-                || attributionFlags == ATTRIBUTION_FLAGS_NONE
-                || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
-            // If this is not a chain, or it is untrusted, return
+        if (active) {
+            // Started callback handles these
             return;
         }
 
-        if (!active) {
-            // if any link in the chain is finished, remove the chain.
-            // TODO ntmyren: be smarter about this
-            mAttributionChains.remove(attributionChainId);
+        // if any link in the chain is finished, remove the chain. Then, find any other chains that
+        // contain this op/package/uid/tag combination, and remove them, as well.
+        // TODO ntmyren: be smarter about this
+        mAttributionChains.remove(attributionChainId);
+        int numChains = mAttributionChains.size();
+        ArrayList<Integer> toRemove = new ArrayList<>();
+        for (int i = 0; i < numChains; i++) {
+            int chainId = mAttributionChains.keyAt(i);
+            ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
+            int chainSize = chain.size();
+            for (int j = 0; j < chainSize; j++) {
+                AccessChainLink link = chain.get(j);
+                if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
+                    toRemove.add(chainId);
+                    break;
+                }
+            }
+        }
+        mAttributionChains.removeAll(toRemove);
+    }
+
+    @Override
+    public void onOpStarted(int op, int uid, String packageName, String attributionTag,
+                @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+       // not part of an attribution chain. Do nothing
+    }
+
+    @Override
+    public void onOpStarted(int op, int uid, String packageName, String attributionTag,
+            @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result,
+            @StartedType int startedType, @AttributionFlags int attributionFlags,
+            int attributionChainId) {
+        if (startedType == START_TYPE_FAILED || attributionChainId == ATTRIBUTION_CHAIN_ID_NONE
+                || attributionFlags == ATTRIBUTION_FLAGS_NONE
+                || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
+            // If this is not a successful start, or it is not a chain, or it is untrusted, return
             return;
         }
+        addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid,
+                attributionTag, attributionFlags, attributionChainId);
+    }
+
+    private void addLinkToChainIfNotPresent(String op, String packageName, int uid,
+            String attributionTag, int attributionFlags, int attributionChainId) {
 
         ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent(
                 attributionChainId, k -> new ArrayList<>());
         AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid,
                 attributionFlags);
 
+        if (currentChain.contains(link)) {
+            return;
+        }
+
         int currSize = currentChain.size();
         if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) {
             // if the list is empty, this link is the end, or the last link in the current chain
@@ -613,5 +657,21 @@
         public boolean isStart() {
             return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0;
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof AccessChainLink)) {
+                return false;
+            }
+            AccessChainLink other = (AccessChainLink) obj;
+            return other.flags == flags && packageAndOpEquals(other.usage.op,
+                    other.usage.packageName, other.usage.attributionTag, other.usage.uid);
+        }
+
+        public boolean packageAndOpEquals(String op, String packageName, String attributionTag,
+                int uid) {
+            return Objects.equals(op, usage.op) && Objects.equals(packageName, usage.packageName)
+                    && Objects.equals(attributionTag, usage.attributionTag) && uid == usage.uid;
+        }
     }
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 7a61511..f477072 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -776,8 +776,10 @@
                 WallpaperColors color = colors.get(i);
                 RectF area = regions.get(i);
                 if (color == null || area == null) {
-                    Log.wtf(TAG, "notifyLocalColorsChanged null values. color: "
-                            + color + " area " + area);
+                    if (DEBUG) {
+                        Log.e(TAG, "notifyLocalColorsChanged null values. color: "
+                                + color + " area " + area);
+                    }
                     continue;
                 }
                 try {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f21d855dd..c1956e4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,6 +33,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.RenderNode;
@@ -237,15 +238,6 @@
             new SurfaceControl.Transaction();
 
     /**
-     * Transaction that should be used for
-     * {@link RenderNode.PositionUpdateListener#positionChanged(long, int, int, int, int)}
-     * The callback is invoked from a thread pool so it's not thread safe with other render thread
-     * transactions. Keep the transactions for position changed callbacks on its own transaction.
-     */
-    private final SurfaceControl.Transaction mPositionChangedTransaction =
-            new SurfaceControl.Transaction();
-
-    /**
      * A temporary transaction holder that should only be used when applying right away. There
      * should be no assumption about thread safety for this transaction.
      */
@@ -295,7 +287,6 @@
             int defStyleRes, boolean disableBackgroundLayer) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mUseBlastAdapter = useBlastAdapter(context);
-        mRenderNode.addPositionUpdateListener(mPositionListener);
 
         setWillNotDraw(true);
         mDisableBackgroundLayer = disableBackgroundLayer;
@@ -943,6 +934,24 @@
         }
     }
 
+
+    // The position update listener is used to safely share the surface size between render thread
+    // workers and the UI thread. Both threads need to know the surface size to determine the scale.
+    // The parent layer scales the surface size to view size. The child (BBQ) layer scales
+    // the buffer to the surface size. Both scales along with the window crop must be applied
+    // synchronously otherwise we may see flickers.
+    // When the listener is updated, we will get at least a single position update call so we can
+    // guarantee any changes we post will be applied.
+    private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight,
+            @Nullable Transaction geometryTransaction) {
+        if (mPositionListener != null) {
+            mRenderNode.removePositionUpdateListener(mPositionListener);
+        }
+        mPositionListener = new SurfaceViewPositionUpdateListener(surfaceWidth, surfaceHeight,
+                geometryTransaction);
+        mRenderNode.addPositionUpdateListener(mPositionListener);
+    }
+
     private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
             boolean creating, boolean sizeChanged, boolean hintChanged) {
         boolean realSizeChanged = false;
@@ -985,13 +994,13 @@
             // While creating the surface, we will set it's initial
             // geometry. Outside of that though, we should generally
             // leave it to the RenderThread.
-            //
-            // There is one more case when the buffer size changes we aren't yet
-            // prepared to sync (as even following the transaction applying
-            // we still need to latch a buffer).
-            // b/28866173
-            if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
-                onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+            Transaction geometryTransaction = new Transaction();
+            geometryTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+            if ((sizeChanged || hintChanged) && !creating) {
+                setBufferSize(geometryTransaction);
+            }
+            if (sizeChanged || creating || !isHardwareAccelerated()) {
+                onSetSurfacePositionAndScaleRT(geometryTransaction, mSurfaceControl,
                         mScreenRect.left, /*positionLeft*/
                         mScreenRect.top /*positionTop*/ ,
                         mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
@@ -1002,17 +1011,31 @@
                 // use SCALING_MODE_SCALE and submit a larger size than the surface
                 // size.
                 if (mClipSurfaceToBounds && mClipBounds != null) {
-                    mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+                    geometryTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
                 } else {
-                    mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+                    geometryTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
                             mSurfaceHeight);
                 }
-            }
-            mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
-            if ((sizeChanged || hintChanged) && !creating) {
-                setBufferSize(mTmpTransaction);
-            }
 
+                boolean applyChangesOnRenderThread =
+                        sizeChanged && !creating && isHardwareAccelerated();
+                if (isHardwareAccelerated()) {
+                    // This will consume the passed in transaction and the transaction will be
+                    // applied on a render worker thread.
+                    replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight,
+                            applyChangesOnRenderThread ? geometryTransaction : null);
+                }
+                if (DEBUG_POSITION) {
+                    Log.d(TAG, String.format(
+                            "%d updateSurfacePosition %s"
+                                + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
+                            System.identityHashCode(this),
+                            applyChangesOnRenderThread ? "RenderWorker" : "UiThread",
+                            mScreenRect.left, mScreenRect.top, mScreenRect.right,
+                            mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
+                }
+            }
+            mTmpTransaction.merge(geometryTransaction);
             mTmpTransaction.apply();
             updateEmbeddedAccessibilityMatrix();
 
@@ -1242,7 +1265,7 @@
             mBlastSurfaceControl.setTransformHint(mTransformHint);
             if (mBlastBufferQueue != null) {
                 mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight,
-                        mFormat);
+                        mFormat, transaction);
             }
         } else {
             transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight);
@@ -1399,19 +1422,6 @@
         mTmpTransaction.apply();
     }
 
-    private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
-            Rect position) {
-        onSetSurfacePositionAndScaleRT(t, surface,
-                position.left /*positionLeft*/,
-                position.top /*positionTop*/,
-                position.width() / (float) mSurfaceWidth /*postScaleX*/,
-                position.height() / (float) mSurfaceHeight /*postScaleY*/);
-
-        if (mViewVisibility) {
-            t.show(surface);
-        }
-    }
-
     /**
      * @return The last render position of the backing surface or an empty rect.
      *
@@ -1421,13 +1431,6 @@
         return mRTLastReportedPosition;
     }
 
-    private void setParentSpaceRectangle(Rect position, long frameNumber, Transaction t) {
-        final ViewRootImpl viewRoot = getViewRootImpl();
-        applySurfaceTransforms(mSurfaceControl, t, position);
-        applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber);
-        applyOrMergeTransaction(t, frameNumber);
-    }
-
     private void applyOrMergeTransaction(Transaction t, long frameNumber) {
         final ViewRootImpl viewRoot = getViewRootImpl();
         boolean useBLAST = viewRoot != null && useBLASTSync(viewRoot);
@@ -1440,9 +1443,24 @@
     }
 
     private Rect mRTLastReportedPosition = new Rect();
+    private Point mRTLastReportedSurfaceSize = new Point();
 
-    private RenderNode.PositionUpdateListener mPositionListener =
-            new RenderNode.PositionUpdateListener() {
+    private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
+        int mRtSurfaceWidth = -1;
+        int mRtSurfaceHeight = -1;
+        private final SurfaceControl.Transaction mPositionChangedTransaction =
+                new SurfaceControl.Transaction();
+        boolean mPendingTransaction = false;
+
+        SurfaceViewPositionUpdateListener(int surfaceWidth, int surfaceHeight,
+                @Nullable Transaction t) {
+            mRtSurfaceWidth = surfaceWidth;
+            mRtSurfaceHeight = surfaceHeight;
+            if (t != null) {
+                mPositionChangedTransaction.merge(t);
+                mPendingTransaction = true;
+            }
+        }
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
@@ -1464,21 +1482,34 @@
             if (mRTLastReportedPosition.left == left
                     && mRTLastReportedPosition.top == top
                     && mRTLastReportedPosition.right == right
-                    && mRTLastReportedPosition.bottom == bottom) {
+                    && mRTLastReportedPosition.bottom == bottom
+                    && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
+                    && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight
+                    && !mPendingTransaction) {
                 return;
             }
             try {
                 if (DEBUG_POSITION) {
                     Log.d(TAG, String.format(
                             "%d updateSurfacePosition RenderWorker, frameNr = %d, "
-                                    + "position = [%d, %d, %d, %d]",
-                            System.identityHashCode(this), frameNumber,
-                            left, top, right, bottom));
+                                    + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
+                            System.identityHashCode(SurfaceView.this), frameNumber,
+                            left, top, right, bottom, mRtSurfaceWidth, mRtSurfaceHeight));
                 }
                 mRTLastReportedPosition.set(left, top, right, bottom);
-                setParentSpaceRectangle(mRTLastReportedPosition, frameNumber,
-                        mPositionChangedTransaction);
-                // Now overwrite mRTLastReportedPosition with our values
+                mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight);
+                onSetSurfacePositionAndScaleRT(mPositionChangedTransaction, mSurfaceControl,
+                        mRTLastReportedPosition.left /*positionLeft*/,
+                        mRTLastReportedPosition.top /*positionTop*/,
+                        mRTLastReportedPosition.width() / (float) mRtSurfaceWidth /*postScaleX*/,
+                        mRTLastReportedPosition.height() / (float) mRtSurfaceHeight /*postScaleY*/);
+                if (mViewVisibility) {
+                    mPositionChangedTransaction.show(mSurfaceControl);
+                }
+                applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
+                        getViewRootImpl().mSurface, frameNumber);
+                applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
+                mPendingTransaction = false;
             } catch (Exception ex) {
                 Log.e(TAG, "Exception from repositionChild", ex);
             }
@@ -1502,7 +1533,13 @@
                         System.identityHashCode(this), frameNumber));
             }
             mRTLastReportedPosition.setEmpty();
-
+            mRTLastReportedSurfaceSize.set(-1, -1);
+            if (mPendingTransaction) {
+                Log.w(TAG, System.identityHashCode(SurfaceView.this)
+                        + "Pending transaction cleared.");
+                mPositionChangedTransaction.clear();
+                mPendingTransaction = false;
+            }
             if (mSurfaceControl == null) {
                 return;
             }
@@ -1521,7 +1558,9 @@
                 mRtHandlingPositionUpdates = false;
             }
         }
-    };
+    }
+
+    private SurfaceViewPositionUpdateListener mPositionListener = null;
 
     private SurfaceHolder.Callback[] getSurfaceCallbacks() {
         SurfaceHolder.Callback[] callbacks;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 38019c9..5f036a3 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1013,8 +1013,10 @@
      *
      * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be
      * hidden, {@code false} when input to the editor resumes and the cursor should be shown again.
-     * @return {@code true} on success, {@code false} if the input connection is no longer valid, or
-     * the protocol is not supported.
+     * @return For editor authors, the return value will always be ignored. For IME authors, this
+     *         method returns {@code true} if the request was sent (whether or not the associated
+     *         editor does something based on this request), {@code false} if the input connection
+     *         is no longer valid.
      */
     default boolean setImeConsumesInput(boolean imeConsumesInput) {
         return false;
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index edd0d16..606f39d 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -18,6 +18,7 @@
 
 import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
 import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS;
+import static android.view.translation.UiTranslationController.DEBUG;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -38,7 +39,6 @@
 import com.android.internal.os.IResultReceiver;
 
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -395,27 +395,26 @@
 
     private static class TranslationResponseCallbackImpl extends ITranslationCallback.Stub {
 
-        private final WeakReference<Consumer<TranslationResponse>> mCallback;
-        private final WeakReference<Executor> mExecutor;
+        private final Consumer<TranslationResponse> mCallback;
+        private final Executor mExecutor;
 
         TranslationResponseCallbackImpl(Consumer<TranslationResponse> callback, Executor executor) {
-            mCallback = new WeakReference<>(callback);
-            mExecutor = new WeakReference<>(executor);
+            mCallback = callback;
+            mExecutor = executor;
         }
 
         @Override
         public void onTranslationResponse(TranslationResponse response) throws RemoteException {
-            final Consumer<TranslationResponse> callback = mCallback.get();
+            if (DEBUG) {
+                Log.i(TAG, "onTranslationResponse called.");
+            }
             final Runnable runnable =
-                    () -> callback.accept(response);
-            if (callback != null) {
-                final Executor executor = mExecutor.get();
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    executor.execute(runnable);
-                } finally {
-                    restoreCallingIdentity(token);
-                }
+                    () -> mCallback.accept(response);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(runnable);
+            } finally {
+                restoreCallingIdentity(token);
             }
         }
     }
diff --git a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
index 3a108e7..06640cb 100644
--- a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
@@ -18,5 +18,6 @@
 
 // Iterface to observe op starts
 oneway interface IAppOpsStartedCallback {
-    void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode);
+    void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode,
+    int startedType, int attributionFlags, int attributionChainId);
 }
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 55a2052..c9a9e51d 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -16,11 +16,9 @@
 
 package com.android.internal.display;
 
-import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.net.Uri;
@@ -63,10 +61,10 @@
                     updateBrightnessFloatFromInt(msg.arg1);
                     break;
                 case MSG_UPDATE_INT:
-                    updateBrightnessIntFromFloat((BrightnessInfo) msg.obj);
+                    updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
                     break;
                 case MSG_UPDATE_BOTH:
-                    updateBoth((BrightnessInfo) msg.obj, Float.intBitsToFloat(msg.arg1));
+                    updateBoth(Float.intBitsToFloat(msg.arg1));
                     break;
                 default:
                     super.handleMessage(msg);
@@ -97,11 +95,11 @@
         brightnessSyncObserver = new BrightnessSyncObserver();
         brightnessSyncObserver.startObserving();
 
-        final BrightnessInfo brightnessInfo = getBrightnessInfo();
+        final float currentFloatBrightness = getScreenBrightnessFloat();
         final int currentIntBrightness = getScreenBrightnessInt(mContext);
 
-        if (brightnessInfo != null && !Float.isNaN(brightnessInfo.brightness)) {
-            updateBrightnessIntFromFloat(brightnessInfo);
+        if (!Float.isNaN(currentFloatBrightness)) {
+            updateBrightnessIntFromFloat(currentFloatBrightness);
         } else if (currentIntBrightness != -1) {
             updateBrightnessFloatFromInt(currentIntBrightness);
         } else {
@@ -114,23 +112,15 @@
 
     /**
      * Converts between the int brightness system and the float brightness system.
-     *
-     * @param brightnessInt The int brightness value to convert.
      */
     public static float brightnessIntToFloat(int brightnessInt) {
-        return brightnessIntToFloat(brightnessInt, null);
-    }
-
-    private static float brightnessIntToFloat(int brightnessInt, BrightnessInfo info) {
         if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
             return PowerManager.BRIGHTNESS_OFF_FLOAT;
         } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
         } else {
-            final float minFloat = info != null
-                    ? info.brightnessMinimum : PowerManager.BRIGHTNESS_MIN;
-            final float maxFloat = info != null
-                    ? info.brightnessMaximum : PowerManager.BRIGHTNESS_MAX;
+            final float minFloat = PowerManager.BRIGHTNESS_MIN;
+            final float maxFloat = PowerManager.BRIGHTNESS_MAX;
             final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
             final float maxInt = PowerManager.BRIGHTNESS_ON;
             return MathUtils.constrainedMap(minFloat, maxFloat, minInt, maxInt, brightnessInt);
@@ -138,28 +128,29 @@
     }
 
     /**
+     * Converts between the float brightness system and the int brightness system.
+     */
+    public static int brightnessFloatToInt(float brightnessFloat) {
+        return Math.round(brightnessFloatToIntRange(brightnessFloat));
+    }
+
+    /**
      * Translates specified value from the float brightness system to the int brightness system,
      * given the min/max of each range. Accounts for special values such as OFF and invalid values.
      * Value returned as a float primitive (to preserve precision), but is a value within the
      * int-system range.
-     *
-     * @param brightnessFloat The float brightness value to convert.
-     * @param info Brightness information to use in the conversion.
      */
-    public static int brightnessFloatToInt(float brightnessFloat, BrightnessInfo info) {
+    public static float brightnessFloatToIntRange(float brightnessFloat) {
         if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
             return PowerManager.BRIGHTNESS_OFF;
         } else if (Float.isNaN(brightnessFloat)) {
             return PowerManager.BRIGHTNESS_INVALID;
         } else {
-            final float minFloat = info != null
-                    ? info.brightnessMinimum : PowerManager.BRIGHTNESS_MIN;
-            final float maxFloat = info != null
-                    ? info.brightnessMaximum : PowerManager.BRIGHTNESS_MAX;
+            final float minFloat = PowerManager.BRIGHTNESS_MIN;
+            final float maxFloat = PowerManager.BRIGHTNESS_MAX;
             final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
             final float maxInt = PowerManager.BRIGHTNESS_ON;
-            return Math.round(MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat,
-                    brightnessFloat));
+            return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat);
         }
     }
 
@@ -194,37 +185,35 @@
      * @param value Brightness value as int to store in the float setting.
      */
     private void updateBrightnessFloatFromInt(int value) {
-        final BrightnessInfo info = getBrightnessInfo();
-        if (brightnessFloatToInt(mPreferredSettingValue, info) == value) {
+        if (brightnessFloatToInt(mPreferredSettingValue) == value) {
             return;
         }
 
-        mPreferredSettingValue = brightnessIntToFloat(value, info);
+        mPreferredSettingValue = brightnessIntToFloat(value);
         final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
         mHandler.removeMessages(MSG_UPDATE_BOTH);
         mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
     }
 
     /**
-     * Updates the settings from the specified {@link BrightnessInfo}. This is called whenever the
-     * float brightness changed from DisplayManager. mPreferredSettingValue holds the most recently
-     * updated brightness value as a float that we would like the display to be set to.
+     * Updates the settings based on a passed in float value. This is called whenever the float
+     * setting changes. mPreferredSettingValue holds the most recently updated brightness value
+     * as a float that we would like the display to be set to.
      *
      * We then schedule an update to both the int and float settings, but, remove all the other
      * messages to update all, to prevent us getting stuck in a loop.
      *
-     * @param brightnessInfo Current brightness information
+     * @param value Brightness setting as float to store in int setting.
      */
-    private void updateBrightnessIntFromFloat(@NonNull BrightnessInfo brightnessInfo) {
-        final float value = brightnessInfo.brightness;
+    private void updateBrightnessIntFromFloat(float value) {
         if (floatEquals(mPreferredSettingValue, value)) {
             return;
         }
 
         mPreferredSettingValue = value;
+        final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
         mHandler.removeMessages(MSG_UPDATE_BOTH);
-        mHandler.obtainMessage(MSG_UPDATE_BOTH, Float.floatToIntBits(value), 0, brightnessInfo)
-                .sendToTarget();
+        mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
     }
 
 
@@ -233,24 +222,16 @@
      * mDisplayManager.setBrightness automatically checks for changes
      * Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class
      *
-     * @param brightnessInfo Brightness information, takes precedent over newBrightnessFloat
      * @param newBrightnessFloat Brightness setting as float to store in both settings
      */
-    private void updateBoth(BrightnessInfo brightnessInfo, float newBrightnessFloat) {
-        int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat, brightnessInfo);
+    private void updateBoth(float newBrightnessFloat) {
+        int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat);
         mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
         if (getScreenBrightnessInt(mContext) != newBrightnessInt) {
             Settings.System.putIntForUser(mContext.getContentResolver(),
                     Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
         }
-    }
 
-    private BrightnessInfo getBrightnessInfo() {
-        final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
-        if (display != null) {
-            return display.getBrightnessInfo();
-        }
-        return null;
     }
 
     /**
@@ -282,15 +263,10 @@
 
             @Override
             public void onDisplayChanged(int displayId) {
-                if (displayId != Display.DEFAULT_DISPLAY) {
-                    return;
-                }
-
-                final BrightnessInfo info = getBrightnessInfo();
-                if (info != null) {
-                    mHandler.removeMessages(MSG_UPDATE_INT);
-                    mHandler.obtainMessage(MSG_UPDATE_INT, info).sendToTarget();
-                }
+                float currentFloat = getScreenBrightnessFloat();
+                int toSend = Float.floatToIntBits(currentFloat);
+                mHandler.removeMessages(MSG_UPDATE_INT);
+                mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
             }
         };
 
diff --git a/core/java/com/android/internal/os/ProcLocksReader.java b/core/java/com/android/internal/os/ProcLocksReader.java
new file mode 100644
index 0000000..bd3115fc5
--- /dev/null
+++ b/core/java/com/android/internal/os/ProcLocksReader.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import com.android.internal.util.ProcFileReader;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Reads and parses {@code locks} files in the {@code proc} filesystem.
+ * A typical example of /proc/locks
+ *
+ * 1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335
+ * 2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF
+ * 2: -> POSIX  ADVISORY  WRITE 18291 fd:09:34062 0 EOF
+ * 2: -> POSIX  ADVISORY  WRITE 18293 fd:09:34062 0 EOF
+ * 3: POSIX  ADVISORY  READ  3888 fd:09:13992 128 128
+ * 4: POSIX  ADVISORY  READ  3888 fd:09:14230 1073741826 1073742335
+ */
+public class ProcLocksReader {
+    private final String mPath;
+
+    public ProcLocksReader() {
+        mPath = "/proc/locks";
+    }
+
+    public ProcLocksReader(String path) {
+        mPath = path;
+    }
+
+    /**
+     * Checks if a process corresponding to a specific pid owns any file locks.
+     * @param pid The process ID for which we want to know the existence of file locks.
+     * @return true If the process holds any file locks, false otherwise.
+     * @throws IOException if /proc/locks can't be accessed.
+     */
+    public boolean hasFileLocks(int pid) throws Exception {
+        ProcFileReader reader = null;
+        long last = -1;
+        long id; // ordinal position of the lock in the list
+        int owner; // the PID of the process that owns the lock
+
+        try {
+            reader = new ProcFileReader(new FileInputStream(mPath));
+
+            while (reader.hasMoreData()) {
+                id = reader.nextLong(true); // lock id
+                if (id == last) {
+                    reader.finishLine(); // blocked lock
+                    continue;
+                }
+
+                reader.nextIgnored(); // lock type: POSIX?
+                reader.nextIgnored(); // lock type: MANDATORY?
+                reader.nextIgnored(); // lock type: RW?
+
+                owner = reader.nextInt(); // pid
+                if (owner == pid) {
+                    return true;
+                }
+                reader.finishLine();
+                last = id;
+            }
+        } catch (IOException e) {
+            // TODO: let ProcFileReader log the failed line
+            throw new Exception("Exception parsing /proc/locks");
+        } finally {
+            IoUtils.closeQuietly(reader);
+        }
+        return false;
+    }
+}
diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java
index ead58c7d..0dd8ad8 100644
--- a/core/java/com/android/internal/util/ProcFileReader.java
+++ b/core/java/com/android/internal/util/ProcFileReader.java
@@ -28,8 +28,8 @@
  * requires each line boundary to be explicitly acknowledged using
  * {@link #finishLine()}. Assumes {@link StandardCharsets#US_ASCII} encoding.
  * <p>
- * Currently doesn't support formats based on {@code \0}, tabs, or repeated
- * delimiters.
+ * Currently doesn't support formats based on {@code \0}, tabs.
+ * Consecutive spaces are treated as a single delimiter.
  */
 public class ProcFileReader implements Closeable {
     private final InputStream mStream;
@@ -75,6 +75,11 @@
     private void consumeBuf(int count) throws IOException {
         // TODO: consider moving to read pointer, but for now traceview says
         // these copies aren't a bottleneck.
+
+        // skip all consecutive delimiters.
+        while (count < mTail && mBuffer[count] == ' ') {
+            count++;
+        }
         System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
         mTail -= count;
         if (mTail == 0) {
@@ -159,11 +164,18 @@
      * Parse and return next token as base-10 encoded {@code long}.
      */
     public long nextLong() throws IOException {
+        return nextLong(false);
+    }
+
+    /**
+     * Parse and return next token as base-10 encoded {@code long}.
+     */
+    public long nextLong(boolean stopAtInvalid) throws IOException {
         final int tokenIndex = nextTokenIndex();
         if (tokenIndex == -1) {
             throw new ProtocolException("Missing required long");
         } else {
-            return parseAndConsumeLong(tokenIndex);
+            return parseAndConsumeLong(tokenIndex, stopAtInvalid);
         }
     }
 
@@ -176,7 +188,7 @@
         if (tokenIndex == -1) {
             return def;
         } else {
-            return parseAndConsumeLong(tokenIndex);
+            return parseAndConsumeLong(tokenIndex, false);
         }
     }
 
@@ -186,7 +198,10 @@
         return s;
     }
 
-    private long parseAndConsumeLong(int tokenIndex) throws IOException {
+    /**
+     * If stopAtInvalid is true, don't throw IOException but return whatever parsed so far.
+     */
+    private long parseAndConsumeLong(int tokenIndex, boolean stopAtInvalid) throws IOException {
         final boolean negative = mBuffer[0] == '-';
 
         // TODO: refactor into something like IntegralToString
@@ -194,7 +209,11 @@
         for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
             final int digit = mBuffer[i] - '0';
             if (digit < 0 || digit > 9) {
-                throw invalidLong(tokenIndex);
+                if (stopAtInvalid) {
+                    break;
+                } else {
+                    throw invalidLong(tokenIndex);
+                }
             }
 
             // always parse as negative number and apply sign later; this
@@ -226,6 +245,18 @@
         return (int) value;
     }
 
+    /**
+     * Bypass the next token.
+     */
+    public void nextIgnored() throws IOException {
+        final int tokenIndex = nextTokenIndex();
+        if (tokenIndex == -1) {
+            throw new ProtocolException("Missing required token");
+        } else {
+            consumeBuf(tokenIndex + 1);
+        }
+    }
+
     @Override
     public void close() throws IOException {
         mStream.close();
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index b46b5a2..d4ae6d7 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -105,9 +105,11 @@
 }
 
 static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
-                         jlong height, jint format) {
+                         jlong height, jint format, jlong transactionPtr) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
-    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
+    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format,
+                  transaction);
 }
 
 static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
@@ -144,7 +146,7 @@
         {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
-        {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
+        {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
         {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue},
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
         {"nativeSetTransactionCompleteCallback",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 16ee6a7..4b93363 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2429,6 +2429,12 @@
     return (jint)nativeToJavaStatus(status);
 }
 
+static jint android_media_AudioSystem_setHotwordDetectionServiceUid(JNIEnv *env, jobject thiz,
+                                                                    jint uid) {
+    status_t status = AudioSystem::setHotwordDetectionServiceUid(uid);
+    return (jint)nativeToJavaStatus(status);
+}
+
 static jint
 android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) {
     std::vector<uid_t> nativeUidsVector;
@@ -2799,6 +2805,8 @@
          {"setSurroundFormatEnabled", "(IZ)I",
           (void *)android_media_AudioSystem_setSurroundFormatEnabled},
          {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+         {"setHotwordDetectionServiceUid", "(I)I",
+          (void *)android_media_AudioSystem_setHotwordDetectionServiceUid},
          {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
          {"isHapticPlaybackSupported", "()Z",
           (void *)android_media_AudioSystem_isHapticPlaybackSupported},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a307369..bad79eb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -550,6 +550,8 @@
     <protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" />
     <protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" />
     <protected-broadcast android:name="com.android.settings.bluetooth.ACTION_DISMISS_PAIRING" />
+    <protected-broadcast android:name="com.android.settings.network.DELETE_SUBSCRIPTION" />
+    <protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" />
     <protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
 
     <protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
@@ -3507,7 +3509,7 @@
          use by third party apps.
          @hide -->
     <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
-        android:protectionLevel="signature|privileged|installer" />
+        android:protectionLevel="signature|privileged|installer|role" />
 
     <!-- @SystemApi Allows an application to update the user app op restrictions.
          Not for use by third party apps.
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index aa050f3..2b9f952 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -26,6 +26,7 @@
           android:layout_width="wrap_content"
           android:layout_gravity="center"
           android:padding="0dp"
+          android:background="@null"
           android:contentDescription="@string/splash_screen_view_icon_description"/>
 
     <View android:id="@+id/splashscreen_branding_view"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cf2f677..0310b18 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -2079,7 +2079,7 @@
     <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع الاتصال"</string>
     <string name="call_notification_incoming_text" msgid="6143109825406638201">"مكالمة واردة"</string>
     <string name="call_notification_ongoing_text" msgid="3880832933933020875">"مكالمة جارية"</string>
-    <string name="call_notification_screening_text" msgid="8396931408268940208">"رصد مكالمة واردة"</string>
+    <string name="call_notification_screening_text" msgid="8396931408268940208">"يتم فحص المكالمة الواردة"</string>
     <plurals name="selected_count" formatted="false" msgid="3946212171128200491">
       <item quantity="zero">تم اختيار <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر</item>
       <item quantity="two">تم اختيار عنصرين (<xliff:g id="COUNT_1">%1$d</xliff:g>)</item>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 542387f..865f026 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -984,7 +984,7 @@
     <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nMöchtest du diese Seite wirklich verlassen?"</string>
     <string name="save_password_label" msgid="9161712335355510035">"Bestätigen"</string>
     <string name="double_tap_toast" msgid="7065519579174882778">"Tipp: Zum Vergrößern und Verkleinern doppeltippen"</string>
-    <string name="autofill_this_form" msgid="3187132440451621492">"AutoFill"</string>
+    <string name="autofill_this_form" msgid="3187132440451621492">"Automatisches Ausfüllen"</string>
     <string name="setup_autofill" msgid="5431369130866618567">"Autom.Ausfüll.konf."</string>
     <string name="autofill_window_title" msgid="4379134104008111961">"Mit <xliff:g id="SERVICENAME">%1$s</xliff:g> automatisch ausfüllen"</string>
     <string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
@@ -1937,7 +1937,7 @@
     <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-Warnung"</string>
     <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeitsprofil"</string>
     <string name="notification_alerted_content_description" msgid="6139691253611265992">"Gewarnt"</string>
-    <string name="notification_verified_content_description" msgid="6401483602782359391">"Bestätigt"</string>
+    <string name="notification_verified_content_description" msgid="6401483602782359391">"Verifiziert"</string>
     <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Maximieren"</string>
     <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Minimieren"</string>
     <string name="expand_action_accessibility" msgid="1947657036871746627">"Maximierung ein-/auschalten"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9a07025..9a29add 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -271,7 +271,7 @@
     <string name="global_action_settings" msgid="4671878836947494217">"Ajustes"</string>
     <string name="global_action_assist" msgid="2517047220311505805">"Asistencia"</string>
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
-    <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo seguro"</string>
+    <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo de seguridad"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt; 999"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
     <string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"Teclado virtual"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 816ddd4..2e4578c 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -29,5 +29,8 @@
     <color name="resolver_empty_state_text">#FFFFFF</color>
     <color name="resolver_empty_state_icon">#FFFFFF</color>
 
+    <color name="call_notification_decline_color">#E66A5E</color>
+    <color name="call_notification_answer_color">#5DBA80</color>
+
     <color name="personal_apps_suspension_notification_color">#8AB4F8</color>
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3f9cee1..2936a08 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1949,7 +1949,7 @@
     <string name="maximize_button_text" msgid="4258922519914732645">"بڑا کریں"</string>
     <string name="close_button_text" msgid="10603510034455258">"بند کریں"</string>
     <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
-    <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب"</string>
+    <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب دیں"</string>
     <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ویڈیو"</string>
     <string name="call_notification_decline_action" msgid="3700345945214000726">"مسترد کریں"</string>
     <string name="call_notification_hang_up_action" msgid="9130720590159188131">"منقطع کر دیں"</string>
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
new file mode 100644
index 0000000..d800c2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.file.Files;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProcLocksReaderTest {
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testRunSimpleLocks() throws Exception {
+        String simpleLocks =
+                "1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335\n" +
+                "2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF\n";
+        assertFalse(runHasFileLocks(simpleLocks, 18402));
+        assertFalse(runHasFileLocks(simpleLocks, 18404));
+        assertTrue(runHasFileLocks(simpleLocks, 18403));
+        assertTrue(runHasFileLocks(simpleLocks, 18292));
+    }
+
+    @Test
+    public void testRunBlockedLocks() throws Exception {
+        String blockedLocks =
+                "1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335\n" +
+                "2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF\n" +
+                "2: -> POSIX  ADVISORY  WRITE 18291 fd:09:34062 0 EOF\n" +
+                "2: -> POSIX  ADVISORY  WRITE 18293 fd:09:34062 0 EOF\n" +
+                "3: POSIX  ADVISORY  READ  3888 fd:09:13992 128 128\n" +
+                "4: POSIX  ADVISORY  READ  3888 fd:09:14230 1073741826 1073742335\n";
+        assertFalse(runHasFileLocks(blockedLocks, 18402));
+        assertFalse(runHasFileLocks(blockedLocks, 18404));
+        assertTrue(runHasFileLocks(blockedLocks, 18403));
+        assertTrue(runHasFileLocks(blockedLocks, 18292));
+
+        assertFalse(runHasFileLocks(blockedLocks, 18291));
+        assertFalse(runHasFileLocks(blockedLocks, 18293));
+        assertTrue(runHasFileLocks(blockedLocks, 3888));
+    }
+
+    private boolean runHasFileLocks(String fileContents, int pid) throws Exception {
+        File tempFile = File.createTempFile("locks", null, mProcDirectory);
+        Files.write(tempFile.toPath(), fileContents.getBytes());
+        boolean result = new ProcLocksReader(tempFile.toString()).hasFileLocks(pid);
+        Files.delete(tempFile.toPath());
+        return result;
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
index b6da195..0532628 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
@@ -166,6 +166,46 @@
         assertEquals(-1L, reader.nextOptionalLong(-1L));
     }
 
+    public void testInvalidLongs() throws Exception {
+        final ProcFileReader reader = buildReader("12: 34\n56 78@#\n");
+
+        assertEquals(12L, reader.nextLong(true));
+        assertEquals(34L, reader.nextLong(true));
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals(56L, reader.nextLong(true));
+        assertEquals(78L, reader.nextLong(true));
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testConsecutiveDelimiters() throws Exception {
+        final ProcFileReader reader = buildReader("1 2  3   4     5\n");
+
+        assertEquals(1L, reader.nextLong());
+        assertEquals(2L, reader.nextLong());
+        assertEquals(3L, reader.nextLong());
+        assertEquals(4L, reader.nextLong());
+        assertEquals(5L, reader.nextLong());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testIgnore() throws Exception {
+        final ProcFileReader reader = buildReader("a b c\n");
+
+        assertEquals("a", reader.nextString());
+        assertTrue(reader.hasMoreData());
+
+        reader.nextIgnored();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals("c", reader.nextString());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
     private static ProcFileReader buildReader(String string) throws IOException {
         return buildReader(string, 2048);
     }
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 6c1c2ee..36215ec 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -33,7 +33,7 @@
     private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
     private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
     private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
-            int format);
+            int format, long transactionPtr);
     private static native void nativeFlushShadowQueue(long ptr);
     private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
                                                               long frameNumber);
@@ -92,9 +92,15 @@
      * @param width The new width for the buffer.
      * @param height The new height for the buffer.
      * @param format The new format for the buffer.
+     * @param t Adds destination frame changes to the passed in transaction.
      */
+    public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format,
+            SurfaceControl.Transaction t) {
+        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, t.mNativeObject);
+    }
+
     public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {
-        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format);
+        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, 0);
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 74fb618..872331c 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -53,6 +53,7 @@
     private long mStartTime;
     private boolean mForceSoftware;
     private Animator mLoopAnimation;
+    private Animator mCurrentAnimation;
 
     RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
             boolean forceSoftware) {
@@ -66,7 +67,7 @@
 
     @NonNull RippleAnimationSession enter(Canvas canvas) {
         mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        if (isHwAccelerated(canvas)) {
+        if (useRTAnimations(canvas)) {
             enterHardware((RecordingCanvas) canvas);
         } else {
             enterSoftware();
@@ -74,8 +75,14 @@
         return this;
     }
 
+    void end() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.end();
+        }
+    }
+
     @NonNull RippleAnimationSession exit(Canvas canvas) {
-        if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas);
+        if (useRTAnimations(canvas)) exitHardware((RecordingCanvas) canvas);
         else exitSoftware();
         return this;
     }
@@ -95,8 +102,12 @@
         return this;
     }
 
-    private boolean isHwAccelerated(Canvas canvas) {
-        return canvas.isHardwareAccelerated() && !mForceSoftware;
+    private boolean useRTAnimations(Canvas canvas) {
+        if (mForceSoftware) return false;
+        if (!canvas.isHardwareAccelerated()) return false;
+        RecordingCanvas hwCanvas = (RecordingCanvas) canvas;
+        if (hwCanvas.mNode == null || !hwCanvas.mNode.isAttached()) return false;
+        return true;
     }
 
     private void exitSoftware() {
@@ -114,10 +125,12 @@
                 if (mLoopAnimation != null) mLoopAnimation.cancel();
                 Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
                 if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+                if (mCurrentAnimation == expand) mCurrentAnimation = null;
             }
         });
         expand.setInterpolator(LINEAR_INTERPOLATOR);
         expand.start();
+        mCurrentAnimation = expand;
     }
 
     private long computeDelay() {
@@ -147,6 +160,7 @@
                 if (mLoopAnimation != null) mLoopAnimation.cancel();
                 Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
                 if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+                if (mCurrentAnimation == exit) mCurrentAnimation = null;
             }
         });
         exit.setTarget(canvas);
@@ -155,6 +169,7 @@
         long delay = computeDelay();
         exit.setStartDelay(delay);
         exit.start();
+        mCurrentAnimation = exit;
     }
 
     private void enterHardware(RecordingCanvas canvas) {
@@ -167,6 +182,7 @@
                 mStartTime + MAX_NOISE_PHASE);
         loop.setTarget(canvas);
         startAnimation(expand, loop);
+        mCurrentAnimation = expand;
     }
 
     private void startAnimation(Animator expand, Animator loop) {
@@ -200,6 +216,7 @@
             mProperties.getShader().setNoisePhase((float) loop.getAnimatedValue());
         });
         startAnimation(expand, loop);
+        mCurrentAnimation = expand;
     }
 
     void setRadius(float radius) {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b994ad2..7354c90 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -49,7 +49,9 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.os.Build;
+import android.os.Looper;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LinearInterpolator;
 
@@ -113,6 +115,7 @@
  * @attr ref android.R.styleable#RippleDrawable_color
  */
 public class RippleDrawable extends LayerDrawable {
+    private static final String TAG = "RippleDrawable";
     /**
      * Radius value that specifies the ripple radius should be computed based
      * on the size of the ripple's container.
@@ -278,6 +281,15 @@
         }
 
         cancelExitingRipples();
+        endPatternedAnimations();
+    }
+
+    private void endPatternedAnimations() {
+        for (int i = 0; i < mRunningAnimations.size(); i++) {
+            RippleAnimationSession session = mRunningAnimations.get(i);
+            session.end();
+        }
+        mRunningAnimations.clear();
     }
 
     private void cancelExitingRipples() {
@@ -291,7 +303,6 @@
             Arrays.fill(ripples, 0, count, null);
         }
         mExitingRipplesCount = 0;
-        mExitingAnimation = true;
         // Always draw an additional "clean" frame after canceling animations.
         invalidateSelf(false);
     }
@@ -714,7 +725,7 @@
         }
 
         cancelExitingRipples();
-        exitPatternedAnimation();
+        endPatternedAnimations();
     }
 
     @Override
@@ -840,6 +851,10 @@
 
     private void startBackgroundAnimation() {
         mRunBackgroundAnimation = false;
+        if (Looper.myLooper() == null) {
+            Log.w(TAG, "Thread doesn't have a looper. Skipping animation.");
+            return;
+        }
         mBackgroundAnimation = ValueAnimator.ofFloat(mBackgroundOpacity, mTargetBackgroundOpacity);
         mBackgroundAnimation.setInterpolator(LINEAR_INTERPOLATOR);
         mBackgroundAnimation.setDuration(BACKGROUND_OPACITY_DURATION);
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 624b8b3..c2b6ffb 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -31,7 +31,7 @@
     <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
     <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
+    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్‌లో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
     <string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index c827932..cb887f2 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -38,6 +38,17 @@
 
     public abstract void updateRingerModeAffectedStreamsInternal();
 
+    /**
+     * Notify the UID of the currently active {@link android.service.voice.HotwordDetectionService}.
+     *
+     * <p>The caller is expected to take care of any performance implications, e.g. by using a
+     * background thread to call this method.</p>
+     *
+     * @param uid UID of the currently active service or {@link android.os.Process#INVALID_UID} if
+     *            none.
+     */
+    public abstract void setHotwordDetectionServiceUid(int uid);
+
     public abstract void setAccessibilityServiceUids(IntArray uids);
 
     /**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8012f03..69d1889 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1769,6 +1769,13 @@
     public static native int setAssistantUid(int uid);
 
     /**
+     * Communicate UID of the current {@link android.service.voice.HotwordDetectionService} to audio
+     * policy service.
+     * @hide
+     */
+    public static native int setHotwordDetectionServiceUid(int uid);
+
+    /**
      * @hide
      * Communicate UIDs of active accessibility services to audio policy service.
      */
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d746c85..37054b8 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1573,6 +1573,9 @@
             if (isFdDuped) {
                 closeFileDescriptor(fileDescriptor);
             }
+            if (modernFd != null) {
+                modernFd.close();
+            }
         }
     }
 
@@ -2554,12 +2557,13 @@
 
     private void initForFilename(String filename) throws IOException {
         FileInputStream in = null;
+        ParcelFileDescriptor modernFd = null;
         mAssetInputStream = null;
         mFilename = filename;
         mIsInputStream = false;
         try {
             in = new FileInputStream(filename);
-            ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(in.getFD());
+            modernFd = FileUtils.convertToModernFd(in.getFD());
             if (modernFd != null) {
                 closeQuietly(in);
                 in = new FileInputStream(modernFd.getFileDescriptor());
@@ -2570,6 +2574,9 @@
             loadAttributes(in);
         } finally {
             closeQuietly(in);
+            if (modernFd != null) {
+                modernFd.close();
+            }
         }
     }
 
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 2943eee..a15529e 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -36,6 +36,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -52,6 +53,8 @@
  * frame and meta data from an input media file.
  */
 public class MediaMetadataRetriever implements AutoCloseable {
+    private static final String TAG = "MediaMetadataRetriever";
+
     // borrowed from ExoPlayer
     private static final String[] STANDARD_GENRES = new String[] {
             // These are the official ID3v1 genres.
@@ -301,11 +304,15 @@
      */
     public void setDataSource(FileDescriptor fd, long offset, long length)
             throws IllegalArgumentException  {
-        ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd);
-        if (modernFd == null) {
-            _setDataSource(fd, offset, length);
-        } else {
-            _setDataSource(modernFd.getFileDescriptor(), offset, length);
+
+        try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
+            if (modernFd == null) {
+                _setDataSource(fd, offset, length);
+            } else {
+                _setDataSource(modernFd.getFileDescriptor(), offset, length);
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Ignoring IO error while setting data source", e);
         }
     }
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4f761ba..26eb2a9 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1271,11 +1271,14 @@
      */
     public void setDataSource(FileDescriptor fd, long offset, long length)
             throws IOException, IllegalArgumentException, IllegalStateException {
-        ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd);
-        if (modernFd == null) {
-            _setDataSource(fd, offset, length);
-        } else {
-            _setDataSource(modernFd.getFileDescriptor(), offset, length);
+        try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
+            if (modernFd == null) {
+                _setDataSource(fd, offset, length);
+            } else {
+                _setDataSource(modernFd.getFileDescriptor(), offset, length);
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Ignoring IO error while setting data source", e);
         }
     }
 
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 628f7ee..7f7fb60 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -292,8 +292,7 @@
         }
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : mRoutes.values()) {
-                if (sessionInfo.getSelectedRoutes().contains(route.getId())
-                        || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+                if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
                     routes.add(route);
                     continue;
                 }
diff --git a/mms/OWNERS b/mms/OWNERS
index befc320..7f05a2a 100644
--- a/mms/OWNERS
+++ b/mms/OWNERS
@@ -2,7 +2,6 @@
 
 tgunn@google.com
 breadley@google.com
-hallliu@google.com
 rgreenwalt@google.com
 amitmahajan@google.com
 fionaxu@google.com
@@ -10,7 +9,10 @@
 jminjie@google.com
 satk@google.com
 shuoq@google.com
-refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
-dbright@google.com
\ No newline at end of file
+xiaotonj@google.com
+huiwang@google.com
+jayachandranc@google.com
+chinmayd@google.com
+amruthr@google.com
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index 5668840..0d23f05 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 tgunn@google.com
 breadley@google.com
-hallliu@google.com
 rgreenwalt@google.com
 amitmahajan@google.com
 fionaxu@google.com
@@ -9,9 +8,11 @@
 jminjie@google.com
 satk@google.com
 shuoq@google.com
-refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
-dbright@google.com
 xiaotonj@google.com
+huiwang@google.com
+jayachandranc@google.com
+chinmayd@google.com
+amruthr@google.com
 
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index 7f258eb..9d3ce34 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -134,6 +134,7 @@
         <receiver
             android:name=".widget.PaintChipsWidget"
             android:label="@string/s_egg_name"
+            android:exported="true"
             android:enabled="false">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -145,4 +146,4 @@
         </receiver>
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/packages/SettingsLib/lint-baseline.xml b/packages/SettingsLib/lint-baseline.xml
index f6d6ca6..d6ea73d 100644
--- a/packages/SettingsLib/lint-baseline.xml
+++ b/packages/SettingsLib/lint-baseline.xml
@@ -892,4 +892,26 @@
             column="59"/>
     </issue>
 
+    <issue
+        id="NewApi"
+        message="Call requires API level S (current min is 29): `android.os.UserManager#isUserForeground`"
+        errorLine1="                .getSystemService(UserManager.class).isUserForeground();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java"
+            line="120"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.os.UserManager#isUserForeground`"
+        errorLine1="                .getSystemService(UserManager.class).isUserForeground();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java"
+            line="120"
+            column="54"/>
+    </issue>
+
 </issues>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b3bdf89..65992a7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -444,7 +444,7 @@
     <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Il telefono potrebbe spegnersi a breve"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Il tablet potrebbe spegnersi a breve"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Il dispositivo potrebbe spegnersi a breve"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index b1aee50..7387985 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -309,7 +309,7 @@
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT аркылуу орнотулган колдонмолордун коопсуздугу текшерилет."</string>
-    <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрсөтүлөт"</string>
+    <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрүнөт"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Алыскы түзмөктөр өтө катуу добуш чыгарып же көзөмөлдөнбөй жатса Bluetooth \"Үндүн абсолюттук деңгээли\" функциясын өчүрөт."</string>
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche функциясынын топтомун иштетет."</string>
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Жакшыртылган туташуу функциясын иштетет."</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fea9601..2bdd617 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -443,8 +443,8 @@
     <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria może się wyczerpać do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+    <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Wkrótce telefon może się wyłączyć"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet może się wkrótce wyłączyć"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Urządzenie może się wkrótce wyłączyć"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a870026..4f74d24 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -489,7 +489,7 @@
     <string name="active_input_method_subtypes" msgid="4232680535471633046">"సక్రియ ఇన్‌పుట్ పద్ధతులు"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"సిస్టమ్ భాషలను ఉపయోగించు"</string>
     <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> యొక్క సెట్టింగ్‌లను తెరవడం విఫలమైంది"</string>
-    <string name="ime_security_warning" msgid="6547562217880551450">"ఈ ఇన్‌పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ్‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> అనువర్తనంలో అందించబడుతుంది. ఈ ఇన్‌పుట్ పద్ధతిని ఉపయోగించాలా?"</string>
+    <string name="ime_security_warning" msgid="6547562217880551450">"ఈ ఇన్‌పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ్‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> యాప్‌లో అందించబడుతుంది. ఈ ఇన్‌పుట్ పద్ధతిని ఉపయోగించాలా?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"గమనిక: రీబూట్ చేసాక, మీరు మీ ఫోన్‌ను అన్‌లాక్ చేసే వరకు ఈ యాప్ ప్రారంభం కాదు"</string>
     <string name="ims_reg_title" msgid="8197592958123671062">"IMS నమోదు స్థితి"</string>
     <string name="ims_reg_status_registered" msgid="884916398194885457">"నమోదు చేయబడింది"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index bd9e0d3..7275d6b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -18,6 +18,9 @@
 
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 
+import static com.android.settingslib.enterprise.ActionDisabledLearnMoreButtonLauncher.DEFAULT_RESOLVE_ACTIVITY_CHECKER;
+import static com.android.settingslib.enterprise.ManagedDeviceActionDisabledByAdminController.DEFAULT_FOREGROUND_USER_CHECKER;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -43,7 +46,11 @@
         } else if (isFinancedDevice(context)) {
             return new FinancedDeviceActionDisabledByAdminController(stringProvider);
         } else {
-            return new ManagedDeviceActionDisabledByAdminController(stringProvider, userHandle);
+            return new ManagedDeviceActionDisabledByAdminController(
+                    stringProvider,
+                    userHandle,
+                    DEFAULT_FOREGROUND_USER_CHECKER,
+                    DEFAULT_RESOLVE_ACTIVITY_CHECKER);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
index 41148797..f9d3aaf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -34,6 +35,17 @@
  */
 public abstract class ActionDisabledLearnMoreButtonLauncher {
 
+    public static ResolveActivityChecker DEFAULT_RESOLVE_ACTIVITY_CHECKER =
+            (packageManager, url, userHandle) -> packageManager.resolveActivityAsUser(
+                    createLearnMoreIntent(url),
+                    PackageManager.MATCH_DEFAULT_ONLY,
+                    userHandle.getIdentifier()) != null;
+
+    interface ResolveActivityChecker {
+        boolean canResolveActivityAsUser(
+                PackageManager packageManager, String url, UserHandle userHandle);
+    }
+
     /**
      * Sets up a "learn more" button which shows a screen with device policy settings
      */
@@ -111,6 +123,14 @@
         finishSelf();
     }
 
+    protected final boolean canLaunchHelpPage(
+            PackageManager packageManager,
+            String url,
+            UserHandle userHandle,
+            ResolveActivityChecker resolveActivityChecker) {
+        return resolveActivityChecker.canResolveActivityAsUser(packageManager, url, userHandle);
+    }
+
     private void showAdminPolicies(Context context, EnforcedAdmin enforcedAdmin) {
         if (enforcedAdmin.component != null) {
             launchShowAdminPolicies(context, enforcedAdmin.user, enforcedAdmin.component);
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index 1472980..e0339da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -64,9 +64,9 @@
         return (dialog, which) -> {
             Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
             final Intent intent = new Intent(ACTION_LEARN_MORE)
-                    .setComponent(enforcedAdmin.component)
                     .putExtra(EXTRA_SETTING_KEY, EXTRA_SETTING_VALUE)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .setPackage(enforcedAdmin.component.getPackageName());
             context.startActivity(intent);
         };
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
index 93e811d..c2034f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
@@ -20,13 +20,14 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
 
 import androidx.annotation.Nullable;
 
-import java.util.Objects;
+import com.android.settingslib.enterprise.ActionDisabledLearnMoreButtonLauncher.ResolveActivityChecker;
 
 
 /**
@@ -35,17 +36,37 @@
 final class ManagedDeviceActionDisabledByAdminController
         extends BaseActionDisabledByAdminController {
 
-    private final UserHandle mUserHandle;
+    interface ForegroundUserChecker {
+        boolean isUserForeground(Context context, UserHandle userHandle);
+    }
+
+    public final static ForegroundUserChecker DEFAULT_FOREGROUND_USER_CHECKER =
+            ManagedDeviceActionDisabledByAdminController::isUserForeground;
+
+    /**
+     * The {@link UserHandle} which is preferred for launching the web help page in
+     * <p>If not able to launch the web help page in this user, the current user will be used as
+     * fallback instead. If the current user cannot open it either, the admin policies page will
+     * be used instead.
+     */
+    private final UserHandle mPreferredUserHandle;
+
+    private final ForegroundUserChecker mForegroundUserChecker;
+    private final ResolveActivityChecker mResolveActivityChecker;
 
     /**
      * Constructs a {@link ManagedDeviceActionDisabledByAdminController}
-     * @param userHandle - user on which to launch the help web page, if necessary
+     * @param preferredUserHandle - user on which to launch the help web page, if necessary
      */
     ManagedDeviceActionDisabledByAdminController(
             DeviceAdminStringProvider stringProvider,
-            UserHandle userHandle) {
+            UserHandle preferredUserHandle,
+            ForegroundUserChecker foregroundUserChecker,
+            ResolveActivityChecker resolveActivityChecker) {
         super(stringProvider);
-        mUserHandle = requireNonNull(userHandle);
+        mPreferredUserHandle = requireNonNull(preferredUserHandle);
+        mForegroundUserChecker = requireNonNull(foregroundUserChecker);
+        mResolveActivityChecker = requireNonNull(resolveActivityChecker);
     }
 
     @Override
@@ -53,14 +74,52 @@
         assertInitialized();
 
         String url = mStringProvider.getLearnMoreHelpPageUrl();
-        if (TextUtils.isEmpty(url)) {
+
+        if (!TextUtils.isEmpty(url)
+                && canLaunchHelpPageInPreferredOrCurrentUser(context, url, mPreferredUserHandle)) {
+            setupLearnMoreButtonToLaunchHelpPage(context, url, mPreferredUserHandle);
+        } else {
             mLauncher.setupLearnMoreButtonToShowAdminPolicies(context, mEnforcementAdminUserId,
                     mEnforcedAdmin);
-        } else {
-            mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, mUserHandle);
         }
     }
 
+    private boolean canLaunchHelpPageInPreferredOrCurrentUser(
+            Context context, String url, UserHandle preferredUserHandle) {
+        PackageManager packageManager = context.getPackageManager();
+        if (mLauncher.canLaunchHelpPage(
+                packageManager, url, preferredUserHandle, mResolveActivityChecker)
+                && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
+            return true;
+        }
+        return mLauncher.canLaunchHelpPage(
+                packageManager, url, context.getUser(), mResolveActivityChecker);
+    }
+
+    /**
+     * Sets up the "Learn more" button to launch the web help page in the {@code
+     * preferredUserHandle} user. If not possible to launch it there, it sets up the button to
+     * launch it in the current user instead.
+     */
+    private void setupLearnMoreButtonToLaunchHelpPage(
+            Context context, String url, UserHandle preferredUserHandle) {
+        PackageManager packageManager = context.getPackageManager();
+        if (mLauncher.canLaunchHelpPage(
+                packageManager, url, preferredUserHandle, mResolveActivityChecker)
+                && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
+            mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, preferredUserHandle);
+        }
+        if (mLauncher.canLaunchHelpPage(
+                packageManager, url, context.getUser(), mResolveActivityChecker)) {
+            mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, context.getUser());
+        }
+    }
+
+    private static boolean isUserForeground(Context context, UserHandle userHandle) {
+        return context.createContextAsUser(userHandle, /* flags= */ 0)
+                .getSystemService(UserManager.class).isUserForeground();
+    }
+
     @Override
     public String getAdminSupportTitle(@Nullable String restriction) {
         if (restriction == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 79446e4..ed8d524 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -44,6 +44,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.RequiresApi;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -56,6 +58,7 @@
 /**
  * InfoMediaManager provide interface to get InfoMediaDevice list.
  */
+@RequiresApi(Build.VERSION_CODES.R)
 public class InfoMediaManager extends MediaManager {
 
     private static final String TAG = "InfoMediaManager";
@@ -145,9 +148,16 @@
     }
 
     private RoutingSessionInfo getRoutingSessionInfo() {
-        final List<RoutingSessionInfo> sessionInfos =
-                mRouterManager.getRoutingSessions(mPackageName);
+        return getRoutingSessionInfo(mPackageName);
+    }
 
+    private RoutingSessionInfo getRoutingSessionInfo(String packageName) {
+        final List<RoutingSessionInfo> sessionInfos =
+                mRouterManager.getRoutingSessions(packageName);
+
+        if (sessionInfos == null || sessionInfos.isEmpty()) {
+            return null;
+        }
         return sessionInfos.get(sessionInfos.size() - 1);
     }
 
@@ -367,33 +377,13 @@
     }
 
     boolean shouldDisableMediaOutput(String packageName) {
-        boolean shouldDisableMediaOutput = false;
         if (TextUtils.isEmpty(packageName)) {
             Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
-            return false;
+            return true;
         }
-        final List<MediaRoute2Info> infos = mRouterManager.getTransferableRoutes(packageName);
-        if (infos.size() == 1) {
-            final MediaRoute2Info info = infos.get(0);
-            final int deviceType = info.getType();
-            switch (deviceType) {
-                case TYPE_UNKNOWN:
-                case TYPE_REMOTE_TV:
-                case TYPE_REMOTE_SPEAKER:
-                case TYPE_GROUP:
-                    shouldDisableMediaOutput = true;
-                    break;
-                default:
-                    shouldDisableMediaOutput = false;
-                    break;
-            }
-        }
-        if (DEBUG) {
-            Log.d(TAG, "shouldDisableMediaOutput() MediaRoute2Info size : " + infos.size()
-                    + ", package name : " + packageName + ", shouldDisableMediaOutput : "
-                    + shouldDisableMediaOutput);
-        }
-        return shouldDisableMediaOutput;
+
+        // Disable when there is no transferable route
+        return mRouterManager.getTransferableRoutes(packageName).isEmpty();
     }
 
     @TargetApi(Build.VERSION_CODES.R)
@@ -456,7 +446,7 @@
     }
 
     private void buildAvailableRoutes() {
-        for (MediaRoute2Info route : mRouterManager.getTransferableRoutes(mPackageName)) {
+        for (MediaRoute2Info route : getAvailableRoutes(mPackageName)) {
             if (DEBUG) {
                 Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
                         + route.getVolume() + ", type : " + route.getType());
@@ -465,6 +455,29 @@
         }
     }
 
+    private List<MediaRoute2Info> getAvailableRoutes(String packageName) {
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
+        if (routingSessionInfo != null) {
+            infos.addAll(mRouterManager.getSelectedRoutes(routingSessionInfo));
+        }
+        final List<MediaRoute2Info> transferableRoutes =
+                mRouterManager.getTransferableRoutes(packageName);
+        for (MediaRoute2Info transferableRoute : transferableRoutes) {
+            boolean alreadyAdded = false;
+            for (MediaRoute2Info mediaRoute2Info : infos) {
+                if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) {
+                    alreadyAdded = true;
+                    break;
+                }
+            }
+            if (!alreadyAdded) {
+                infos.add(transferableRoute);
+            }
+        }
+        return infos;
+    }
+
     @VisibleForTesting
     void addMediaDevice(MediaRoute2Info route) {
         final int deviceType = route.getType();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index a8da2c0..22001c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -22,11 +22,13 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.media.RoutingSessionInfo;
+import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.A2dpProfile;
@@ -49,6 +51,7 @@
 /**
  * LocalMediaManager provide interface to get MediaDevice list and transfer media to MediaDevice.
  */
+@RequiresApi(Build.VERSION_CODES.R)
 public class LocalMediaManager implements BluetoothCallback {
     private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
     private static final String TAG = "LocalMediaManager";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
index d9be4f3..509e12d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
@@ -30,6 +30,8 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
 import android.os.UserManager;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -45,9 +47,11 @@
 @RunWith(RobolectricTestRunner.class)
 public class ManagedDeviceActionDisabledByAdminControllerTest {
 
+    private static UserHandle MANAGED_USER = UserHandle.of(123);
     private static final String RESTRICTION = UserManager.DISALLOW_ADJUST_VOLUME;
     private static final String EMPTY_URL = "";
     private static final String SUPPORT_TITLE_FOR_RESTRICTION = DISALLOW_ADJUST_VOLUME_TITLE;
+    public static final ResolveInfo TEST_RESULT_INFO = new ResolveInfo();
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private final Activity mActivity = ActivityController.of(new Activity()).get();
@@ -60,8 +64,21 @@
     }
 
     @Test
-    public void setupLearnMoreButton_validUrl_negativeButtonSet() {
-        ManagedDeviceActionDisabledByAdminController controller = createController(URL);
+    public void setupLearnMoreButton_noUrl_negativeButtonSet() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(EMPTY_URL);
+
+        controller.setupLearnMoreButton(mContext);
+
+        mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
+    }
+
+    @Test
+    public void setupLearnMoreButton_validUrl_foregroundUser_launchesHelpPage() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ true,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ MANAGED_USER);
 
         controller.setupLearnMoreButton(mContext);
 
@@ -69,8 +86,38 @@
     }
 
     @Test
-    public void setupLearnMoreButton_noUrl_negativeButtonSet() {
-        ManagedDeviceActionDisabledByAdminController controller = createController(EMPTY_URL);
+    public void setupLearnMoreButton_validUrl_browserInPreferredUser_notForeground_showsAdminPolicies() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ false,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ MANAGED_USER);
+
+        controller.setupLearnMoreButton(mContext);
+
+        mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
+    }
+
+    @Test
+    public void setupLearnMoreButton_validUrl_browserInCurrentUser_launchesHelpPage() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ false,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ mContext.getUser());
+
+        controller.setupLearnMoreButton(mContext);
+
+        mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_LAUNCH_HELP_PAGE);
+    }
+
+    @Test
+    public void setupLearnMoreButton_validUrl_browserNotOnAnyUser_showsAdminPolicies() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ false,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ null);
 
         controller.setupLearnMoreButton(mContext);
 
@@ -110,13 +157,33 @@
     }
 
     private ManagedDeviceActionDisabledByAdminController createController() {
-        return createController(/* url= */ null);
+        return createController(
+                /* url= */ null,
+                /* foregroundUserChecker= */ true,
+                mContext.getUser(),
+                /* userContainingBrowser= */ null);
     }
 
     private ManagedDeviceActionDisabledByAdminController createController(String url) {
+        return createController(
+                url,
+                /* foregroundUserChecker= */ true,
+                mContext.getUser(),
+                /* userContainingBrowser= */ null);
+    }
+
+    private ManagedDeviceActionDisabledByAdminController createController(
+            String url,
+            boolean isUserForeground,
+            UserHandle preferredUserHandle,
+            UserHandle userContainingBrowser) {
         ManagedDeviceActionDisabledByAdminController controller =
                 new ManagedDeviceActionDisabledByAdminController(
-                        new FakeDeviceAdminStringProvider(url), mContext.getUser());
+                        new FakeDeviceAdminStringProvider(url),
+                        preferredUserHandle,
+                        /* foregroundUserChecker= */ (context, userHandle) -> isUserForeground,
+                        /* resolveActivityChecker= */ (packageManager, __, userHandle) ->
+                                userHandle.equals(userContainingBrowser));
         controller.initialize(mTestUtils.createLearnMoreButtonLauncher());
         controller.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
         return controller;
diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml
index 2b0ab6f..558ec08 100644
--- a/packages/SystemUI/res/drawable/fingerprint_bg.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml
@@ -14,10 +14,11 @@
 -->
 <shape
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="oval">
 
     <solid
-      android:color="?android:attr/colorBackground"/>
+      android:color="?androidprv:attr/colorSurface"/>
 
     <size
         android:width="64dp"
diff --git a/packages/SystemUI/res/drawable/ic_unlock.xml b/packages/SystemUI/res/drawable/ic_unlock.xml
new file mode 100644
index 0000000..c3b3469
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_unlock.xml
@@ -0,0 +1,42 @@
+<!--
+     Copyright (C) 2021 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:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+    <group android:translateX="8.625" android:translateY="13.625">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
+    </group>
+    <group android:translateX="14" android:translateY="13.5">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+    </group>
+    <group android:translateX="20" android:translateY="35.75">
+        <path
+            android:fillColor="#FF000000"
+            android:fillAlpha="1"
+            android:fillType="nonZero"
+            android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
+    </group>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 2789ed1..eb76382 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -30,6 +30,7 @@
         android:id="@+id/status_icon_area"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:paddingEnd="@dimen/system_icons_keyguard_padding_end"
         android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_alignParentEnd="true"
         android:gravity="center_vertical|end" >
@@ -38,12 +39,10 @@
             android:layout_height="match_parent"
             android:layout_weight="1"
             android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
-            android:gravity="center_vertical|end"
-            android:paddingEnd="@dimen/system_icons_keyguard_padding_end" >
+            android:gravity="center_vertical|end">
             <include layout="@layout/system_icons" />
         </FrameLayout>
 
-
         <ImageView android:id="@+id/multi_user_avatar"
             android:layout_width="@dimen/multi_user_avatar_keyguard_size"
             android:layout_height="@dimen/multi_user_avatar_keyguard_size"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 1a91202..b0f1f48 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,9 +55,23 @@
         android:id="@+id/lock_icon_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="48px"
-        android:layout_gravity="center"
-        android:scaleType="centerCrop"/>
+        android:layout_gravity="center">
+        <!-- Background protection -->
+        <ImageView
+            android:id="@+id/lock_icon_bg"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@drawable/fingerprint_bg"
+            android:visibility="invisible"/>
+
+        <ImageView
+            android:id="@+id/lock_icon"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="48px"
+            android:layout_gravity="center"
+            android:scaleType="centerCrop"/>
+    </com.android.keyguard.LockIconView>
 
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
index 8834ac0..18517906 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -26,8 +26,7 @@
         android:id="@+id/udfps_keyguard_fp_bg"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@drawable/fingerprint_bg"
-        android:visibility="gone"/>
+        android:src="@drawable/fingerprint_bg"/>
 
     <!-- Fingerprint -->
     <!-- AOD dashed fingerprint icon with moving dashes -->
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7d00c5b..e17f032e 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -799,7 +799,7 @@
       <item quantity="other">%d分</item>
       <item quantity="one">%d分</item>
     </plurals>
-    <string name="battery_panel_title" msgid="5931157246673665963">"電池の使用状況"</string>
+    <string name="battery_panel_title" msgid="5931157246673665963">"バッテリーの使用状況"</string>
     <string name="battery_detail_charging_summary" msgid="8821202155297559706">"充電中はバッテリー セーバーは利用できません"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"バッテリー セーバー"</string>
     <string name="battery_detail_switch_summary" msgid="3668748557848025990">"パフォーマンスとバックグラウンド データを制限します"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index f62d9da..8a26ed3 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -707,11 +707,11 @@
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Эскертмелерди башкаруу каражаттары"</string>
     <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"Күйүк"</string>
     <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"Өчүк"</string>
-    <string name="power_notification_controls_description" msgid="1334963837572708952">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрсөтүлөт \n- Билдирмелер толук экранда көрсөтүлөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрсөтүлөт \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык билдирмелер бөгөттөлөт"</string>
+    <string name="power_notification_controls_description" msgid="1334963837572708952">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрүнөт \n- Билдирмелер толук экранда көрүнөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрүнөт \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык билдирмелер бөгөттөлөт"</string>
     <string name="notification_header_default_channel" msgid="225454696914642444">"Билдирмелер"</string>
     <string name="notification_channel_disabled" msgid="928065923928416337">"Мындан ары бул билдирмелер сизге көрүнбөйт"</string>
     <string name="notification_channel_minimized" msgid="6892672757877552959">"Бул билдирмелер кичирейтилет"</string>
-    <string name="notification_channel_silenced" msgid="1995937493874511359">"Бул билдирмелер үнсүз көрсөтүлөт"</string>
+    <string name="notification_channel_silenced" msgid="1995937493874511359">"Бул билдирмелер үнсүз көрүнөт"</string>
     <string name="notification_channel_unsilenced" msgid="94878840742161152">"Бул билдирмелер тууралуу кабарлап турабыз"</string>
     <string name="inline_blocking_helper" msgid="2891486013649543452">"Адатта мындай билдирмелерди өткөрүп жибересиз. \nАлар көрүнө берсинби?"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index c503ebd..40ceb59 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -452,8 +452,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
     <string name="tap_again" msgid="1315420114387908655">"फेरि ट्याप गर्नुहोस्"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string>
-    <!-- no translation found for keyguard_unlock_press (8488350566398524740) -->
-    <skip />
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"अनलक गर्न प्रेस गर्नुहोस्"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"फेरि प्रयास गर्न माथितिर स्वाइप गर्नुहोस्"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC प्रयोग गर्न स्क्रिन अनलक गर्नुहोस्"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"यो डिभाइस तपाईंको सङ्गठनको स्वामित्वमा छ"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 6845b5b..a948fae 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -452,8 +452,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Trokit përsëri për ta hapur"</string>
     <string name="tap_again" msgid="1315420114387908655">"Trokit sërish"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string>
-    <!-- no translation found for keyguard_unlock_press (8488350566398524740) -->
-    <skip />
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Shtyp për të hapur"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Rrëshqit lart për të provuar përsëri"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Shkyçe për të përdorur NFC-në"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Kjo pajisje i përket organizatës sate"</string>
diff --git a/packages/SystemUI/res/values-w390dp/config.xml b/packages/SystemUI/res/values-w390dp/config.xml
new file mode 100644
index 0000000..222d848
--- /dev/null
+++ b/packages/SystemUI/res/values-w390dp/config.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+
+<resources>
+    <bool name="config_showBatteryEstimateQSBH">true</bool>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f5d47ce..11850b4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -539,6 +539,11 @@
         <item>@*android:string/status_bar_headset</item>
     </string-array>
 
+
+    <!-- Whether to show estimate in QS header. Default to false in case there's not enough
+     space -->
+    <bool name="config_showBatteryEstimateQSBH">false</bool>
+
     <!-- A path similar to frameworks/base/core/res/res/values/config.xml
       config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
       cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 04e5dd1..a670216 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -438,6 +438,8 @@
     <string name="fingerprint_dialog_use_fingerprint">Use your fingerprint to continue</string>
     <!-- Message shown to ask the user to use screenlock to continue.[CHAR LIMIT=NONE] -->
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock">Can\u2019t recognize fingerprint. Use screen lock instead.</string>
+    <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
+    <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string>
 
     <!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] -->
     <string name="face_dialog_looking_for_face">Looking for you\u2026</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 90c1e40..3a3d308 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -209,6 +209,11 @@
                     @Override
                     public void onCancelled(
                             @Nullable WindowInsetsAnimationController controller) {
+                        // It is possible to be denied control of ime insets, which means onReady
+                        // is never called. We still need to notify the runnables in order to
+                        // complete the bouncer disappearing
+                        runOnFinishImeAnimationRunnable();
+                        finishRunnable.run();
                     }
                 });
         return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9a9e4a9..0bb5b1c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -149,7 +149,7 @@
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_SPEW = false;
-    private static final int LOW_BATTERY_THRESHOLD = 20;
+    private static final int FINGERPRINT_LOCKOUT_RESET_DELAY_MS = 600;
 
     private static final String ACTION_FACE_UNLOCK_STARTED
             = "com.android.facelock.FACE_UNLOCK_STARTED";
@@ -347,13 +347,16 @@
     private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
     private static final int HAL_ERROR_RETRY_MAX = 20;
 
-    private final Runnable mCancelNotReceived = new Runnable() {
-        @Override
-        public void run() {
-            Log.w(TAG, "Cancel not received, transitioning to STOPPED");
-            mFingerprintRunningState = mFaceRunningState = BIOMETRIC_STATE_STOPPED;
-            updateBiometricListeningState();
-        }
+    private final Runnable mFpCancelNotReceived = () -> {
+        Log.e(TAG, "Fp cancellation not received, transitioning to STOPPED");
+        mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+        updateFingerprintListeningState();
+    };
+
+    private final Runnable mFaceCancelNotReceived = () -> {
+        Log.e(TAG, "Face cancellation not received, transitioning to STOPPED");
+        mFaceRunningState = BIOMETRIC_STATE_STOPPED;
+        updateFaceListeningState();
     };
 
     private final Handler mHandler;
@@ -791,19 +794,19 @@
 
     private void handleFingerprintError(int msgId, String errString) {
         Assert.isMainThread();
-        if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED && mHandler.hasCallbacks(
-                mCancelNotReceived)) {
-            mHandler.removeCallbacks(mCancelNotReceived);
+        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+            mHandler.removeCallbacks(mFpCancelNotReceived);
         }
 
+        // Error is always the end of authentication lifecycle.
+        mFingerprintCancelSignal = null;
+
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
                 && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
             updateFingerprintListeningState();
         } else {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
-            mFingerprintCancelSignal = null;
-            mFaceCancelSignal = null;
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
@@ -834,7 +837,18 @@
     private void handleFingerprintLockoutReset() {
         mFingerprintLockedOut = false;
         mFingerprintLockedOutPermanent = false;
-        updateFingerprintListeningState();
+
+        if (isUdfpsEnrolled()) {
+            // TODO(b/194825098): update the reset signal(s)
+            // A successful unlock will trigger a lockout reset, but there is no guarantee
+            // that the events will arrive in a particular order. Add a delay here in case
+            // an unlock is in progress. In this is a normal unlock the extra delay won't
+            // be noticeable.
+            mHandler.postDelayed(this::updateFingerprintListeningState,
+                    FINGERPRINT_LOCKOUT_RESET_DELAY_MS);
+        } else {
+            updateFingerprintListeningState();
+        }
     }
 
     private void setFingerprintRunningState(int fingerprintRunningState) {
@@ -894,6 +908,7 @@
 
     private void handleFaceAuthFailed() {
         Assert.isMainThread();
+        mFaceCancelSignal = null;
         setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -972,10 +987,13 @@
     private void handleFaceError(int msgId, String errString) {
         Assert.isMainThread();
         if (DEBUG_FACE) Log.d(TAG, "Face error received: " + errString);
-        if (msgId == FaceManager.FACE_ERROR_CANCELED && mHandler.hasCallbacks(mCancelNotReceived)) {
-            mHandler.removeCallbacks(mCancelNotReceived);
+        if (mHandler.hasCallbacks(mFaceCancelNotReceived)) {
+            mHandler.removeCallbacks(mFaceCancelNotReceived);
         }
 
+        // Error is always the end of authentication lifecycle
+        mFaceCancelSignal = null;
+
         if (msgId == FaceManager.FACE_ERROR_CANCELED
                 && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
             setFaceRunningState(BIOMETRIC_STATE_STOPPED);
@@ -2357,6 +2375,14 @@
     }
 
     private void startListeningForFingerprint() {
+        final int userId = getCurrentUser();
+        final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
+        if (mFingerprintCancelSignal != null) {
+            Log.e(TAG, "Cancellation signal is not null, high chance of bug in fp auth lifecycle"
+                    + " management. FP state: " + mFingerprintRunningState
+                    + ", unlockPossible: " + unlockPossible);
+        }
+
         if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
             setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
@@ -2366,11 +2392,8 @@
             return;
         }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
-        int userId = getCurrentUser();
-        if (isUnlockWithFingerprintPossible(userId)) {
-            if (mFingerprintCancelSignal != null) {
-                mFingerprintCancelSignal.cancel();
-            }
+
+        if (unlockPossible) {
             mFingerprintCancelSignal = new CancellationSignal();
 
             if (isEncryptedOrLockdown(userId)) {
@@ -2386,6 +2409,14 @@
     }
 
     private void startListeningForFace() {
+        final int userId = getCurrentUser();
+        final boolean unlockPossible = isUnlockWithFacePossible(userId);
+        if (mFaceCancelSignal != null) {
+            Log.e(TAG, "Cancellation signal is not null, high chance of bug in face auth lifecycle"
+                    + " management. Face state: " + mFaceRunningState
+                    + ", unlockPossible: " + unlockPossible);
+        }
+
         if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
             setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
@@ -2394,11 +2425,8 @@
             return;
         }
         if (DEBUG) Log.v(TAG, "startListeningForFace(): " + mFaceRunningState);
-        int userId = getCurrentUser();
-        if (isUnlockWithFacePossible(userId)) {
-            if (mFaceCancelSignal != null) {
-                mFaceCancelSignal.cancel();
-            }
+
+        if (unlockPossible) {
             mFaceCancelSignal = new CancellationSignal();
 
             // This would need to be updated for multi-sensor devices
@@ -2450,9 +2478,8 @@
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
                 mFingerprintCancelSignal = null;
-                if (!mHandler.hasCallbacks(mCancelNotReceived)) {
-                    mHandler.postDelayed(mCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-                }
+                mHandler.removeCallbacks(mFpCancelNotReceived);
+                mHandler.postDelayed(mFpCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
             }
             setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
         }
@@ -2467,9 +2494,8 @@
             if (mFaceCancelSignal != null) {
                 mFaceCancelSignal.cancel();
                 mFaceCancelSignal = null;
-                if (!mHandler.hasCallbacks(mCancelNotReceived)) {
-                    mHandler.postDelayed(mCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-                }
+                mHandler.removeCallbacks(mFaceCancelNotReceived);
+                mHandler.postDelayed(mFaceCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
             }
             setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
         }
@@ -2831,6 +2857,11 @@
             mSecureCameraLaunched = false;
         }
 
+        if (mKeyguardBypassController != null) {
+            // LS visibility has changed, so reset deviceEntryIntent
+            mKeyguardBypassController.setUserHasDeviceEntryIntent(false);
+        }
+
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index c1d448d..622419a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -17,15 +17,21 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.PointF;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -33,16 +39,47 @@
 /**
  * A view positioned under the notification shade.
  */
-public class LockIconView extends ImageView implements Dumpable {
+public class LockIconView extends FrameLayout implements Dumpable {
     @NonNull private final RectF mSensorRect;
     @NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
     private int mRadius;
 
+    private ImageView mLockIcon;
+    private ImageView mUnlockBgView;
+
+    private int mLockIconColor;
+
     public LockIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSensorRect = new RectF();
     }
 
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mLockIcon = findViewById(R.id.lock_icon);
+        mUnlockBgView = findViewById(R.id.lock_icon_bg);
+    }
+
+    void updateColorAndBackgroundVisibility(boolean useBackground) {
+        if (useBackground) {
+            mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+                    android.R.attr.textColorPrimary);
+            mUnlockBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+            mUnlockBgView.setVisibility(View.VISIBLE);
+        } else {
+            mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+                    R.attr.wallpaperTextColorAccent);
+            mUnlockBgView.setVisibility(View.GONE);
+        }
+
+        mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
+    }
+
+    void setImageDrawable(Drawable drawable) {
+        mLockIcon.setImageDrawable(drawable);
+    }
+
     void setCenterLocation(@NonNull PointF center, int radius) {
         mLockIconCenter = center;
         mRadius = radius;
@@ -56,9 +93,12 @@
 
         setX(mSensorRect.left);
         setY(mSensorRect.top);
-        setLayoutParams(new FrameLayout.LayoutParams(
+
+        final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                 (int) (mSensorRect.right - mSensorRect.left),
-                (int) (mSensorRect.bottom - mSensorRect.top)));
+                (int) (mSensorRect.bottom - mSensorRect.top));
+        lp.gravity = Gravity.CENTER;
+        setLayoutParams(lp);
     }
 
     @Override
@@ -70,7 +110,6 @@
         return mLockIconCenter.y - mRadius;
     }
 
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 9c8582fa..4317e25 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -43,7 +43,6 @@
 import androidx.annotation.Nullable;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
-import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
@@ -97,8 +96,8 @@
     @NonNull private final AnimatedVectorDrawable mLockToUnlockIcon;
     @NonNull private final Drawable mLockIcon;
     @NonNull private final Drawable mUnlockIcon;
-    @NonNull private final CharSequence mUnlockedLabel;
-    @NonNull private final CharSequence mLockedLabel;
+    @NonNull private CharSequence mUnlockedLabel;
+    @NonNull private CharSequence mLockedLabel;
     @Nullable private final Vibrator mVibrator;
 
     private boolean mIsDozing;
@@ -111,7 +110,7 @@
     private boolean mUserUnlockedWithBiometric;
     private Runnable mCancelDelayedUpdateVisibilityRunnable;
 
-    private boolean mHasUdfps;
+    private boolean mUdfpsSupported;
     private float mHeightPixels;
     private float mWidthPixels;
     private int mBottomPadding; // in pixels
@@ -152,9 +151,8 @@
 
         final Context context = view.getContext();
         mUnlockIcon = mView.getContext().getResources().getDrawable(
-            R.anim.lock_to_unlock,
+            R.drawable.ic_unlock,
             mView.getContext().getTheme());
-        ((AnimatedVectorDrawable) mUnlockIcon).start();
         mLockIcon = mView.getContext().getResources().getDrawable(
                 R.anim.lock_to_unlock,
                 mView.getContext().getTheme());
@@ -177,7 +175,7 @@
     protected void onViewAttached() {
         // we check this here instead of onInit since the FingerprintManager + FaceManager may not
         // have started up yet onInit
-        mHasUdfps = mAuthController.getUdfpsSensorLocation() != null;
+        mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
 
         updateConfiguration();
         updateKeyguardShowing();
@@ -307,12 +305,7 @@
     }
 
     private void updateColors() {
-        final int color = Utils.getColorAttrDefaultColor(mView.getContext(),
-                R.attr.wallpaperTextColorAccent);
-        mFpToUnlockIcon.setTint(color);
-        mLockToUnlockIcon.setTint(color);
-        mLockIcon.setTint(color);
-        mUnlockIcon.setTint(color);
+        mView.updateColorAndBackgroundVisibility(mUdfpsSupported);
     }
 
     private void updateConfiguration() {
@@ -321,11 +314,17 @@
         mHeightPixels = metrics.heightPixels;
         mBottomPadding = mView.getContext().getResources().getDimensionPixelSize(
                 R.dimen.lock_icon_margin_bottom);
+
+        mUnlockedLabel = mView.getContext().getResources().getString(
+                R.string.accessibility_unlock_button);
+        mLockedLabel = mView.getContext()
+                .getResources().getString(R.string.accessibility_lock_icon);
+
         updateLockIconLocation();
     }
 
     private void updateLockIconLocation() {
-        if (mHasUdfps) {
+        if (mUdfpsSupported) {
             FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
             mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
                     props.sensorRadius);
@@ -467,6 +466,7 @@
         @Override
         public void onConfigChanged(Configuration newConfig) {
             updateConfiguration();
+            updateColors();
         }
     };
 
@@ -560,7 +560,7 @@
     }
 
     private boolean isClickable() {
-        return mUdfpsEnrolled || mShowUnlockIcon;
+        return mUdfpsSupported || mShowUnlockIcon;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 54e78cf..deceb95 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -195,6 +195,7 @@
      * @param mode desired mode (none, on, off)
      */
     public void setPercentShowMode(@BatteryPercentMode int mode) {
+        if (mode == mShowPercentMode) return;
         mShowPercentMode = mode;
         updateShowPercent();
     }
@@ -330,7 +331,7 @@
                     if (mBatteryPercentView == null) {
                         return;
                     }
-                    if (estimate != null) {
+                    if (estimate != null && mShowPercentMode == MODE_ESTIMATE) {
                         mBatteryPercentView.setText(estimate);
                         setContentDescription(getContext().getString(
                                 R.string.accessibility_battery_level_with_estimate,
@@ -485,6 +486,7 @@
         pw.println("    mTextColor: #" + Integer.toHexString(mTextColor));
         pw.println("    mBatteryStateUnknown: " + mBatteryStateUnknown);
         pw.println("    mLevel: " + mLevel);
+        pw.println("    mMode: " + mShowPercentMode);
     }
 
     private final class SettingObserver extends ContentObserver {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
index 205054d..98ad875 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
@@ -93,6 +93,8 @@
     @Nullable private ModalityListener mModalityListener;
     @Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps;
     @Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
+    @Nullable @VisibleForTesting UdfpsIconController mUdfpsIconController;
+
 
     public AuthBiometricFaceToFingerprintView(Context context) {
         super(context);
@@ -107,6 +109,12 @@
         super(context, attrs, injector);
     }
 
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mUdfpsIconController = new UdfpsIconController(mContext, mIconView, mIndicatorView);
+    }
+
     @Modality
     int getActiveSensorType() {
         return mActiveSensorType;
@@ -168,35 +176,26 @@
     }
 
     @Override
-    @NonNull
-    protected IconController getIconController() {
-        if (mActiveSensorType == TYPE_FINGERPRINT) {
-            if (!(mIconController instanceof UdfpsIconController)) {
-                mIconController = createUdfpsIconController();
-            }
-            return mIconController;
-        }
-        return super.getIconController();
-    }
-
-    @NonNull
-    protected IconController createUdfpsIconController() {
-        return new UdfpsIconController(getContext(), mIconView, mIndicatorView);
-    }
-
-    @Override
     public void updateState(@BiometricState int newState) {
-        if (mState == STATE_HELP || mState == STATE_ERROR) {
-            @Modality final int currentType = mActiveSensorType;
-            mActiveSensorType = TYPE_FINGERPRINT;
+        if (mActiveSensorType == TYPE_FACE) {
+            if (newState == STATE_HELP || newState == STATE_ERROR) {
+                mActiveSensorType = TYPE_FINGERPRINT;
 
-            setRequireConfirmation(false);
-            mConfirmButton.setEnabled(false);
-            mConfirmButton.setVisibility(View.GONE);
+                setRequireConfirmation(false);
+                mConfirmButton.setEnabled(false);
+                mConfirmButton.setVisibility(View.GONE);
 
-            if (mModalityListener != null && currentType != mActiveSensorType) {
-                mModalityListener.onModalitySwitched(currentType, mActiveSensorType);
+                if (mModalityListener != null) {
+                    mModalityListener.onModalitySwitched(TYPE_FACE, mActiveSensorType);
+                }
+
+                // Deactivate the face icon controller so it stops drawing to the view
+                mFaceIconController.deactivate();
+                // Then, activate this icon controller. We need to start in the "error" state
+                mUdfpsIconController.updateState(mState, newState);
             }
+        } else { // Fingerprint
+            mUdfpsIconController.updateState(mState, newState);
         }
 
         super.updateState(newState);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index e8da7c5..c32c1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -49,6 +49,7 @@
         protected Handler mHandler;
         protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
         protected @BiometricState int mState;
+        protected boolean mDeactivated;
 
         protected IconController(Context context, ImageView iconView, TextView textView) {
             mContext = context;
@@ -67,6 +68,11 @@
         }
 
         protected void animateIcon(int iconRes, boolean repeat) {
+            Log.d(TAG, "animateIcon, state: " + mState + ", deactivated: " + mDeactivated);
+            if (mDeactivated) {
+                return;
+            }
+
             final AnimatedVectorDrawable icon =
                     (AnimatedVectorDrawable) mContext.getDrawable(iconRes);
             mIconView.setImageDrawable(icon);
@@ -92,12 +98,26 @@
         @Override
         public void onAnimationEnd(Drawable drawable) {
             super.onAnimationEnd(drawable);
+            Log.d(TAG, "onAnimationEnd, mState: " + mState + ", deactivated: " + mDeactivated);
+            if (mDeactivated) {
+                return;
+            }
+
             if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) {
                 pulseInNextDirection();
             }
         }
 
+        protected void deactivate() {
+            mDeactivated = true;
+        }
+
         protected void updateState(int lastState, int newState) {
+            if (mDeactivated) {
+                Log.w(TAG, "Ignoring updateState when deactivated: " + newState);
+                return;
+            }
+
             final boolean lastStateIsErrorIcon =
                     lastState == STATE_ERROR || lastState == STATE_HELP;
 
@@ -142,7 +162,7 @@
         }
     }
 
-    protected IconController mIconController;
+    @Nullable @VisibleForTesting IconController mFaceIconController;
 
     public AuthBiometricFaceView(Context context) {
         this(context, null);
@@ -158,6 +178,12 @@
     }
 
     @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mFaceIconController = new IconController(mContext, mIconView, mIndicatorView);
+    }
+
+    @Override
     protected int getDelayAfterAuthenticatedDurationMs() {
         return HIDE_DELAY_MS;
     }
@@ -187,17 +213,9 @@
         return true;
     }
 
-    @NonNull
-    protected IconController getIconController() {
-        if (mIconController == null) {
-            mIconController = new IconController(mContext, mIconView, mIndicatorView);
-        }
-        return mIconController;
-    }
-
     @Override
     public void updateState(@BiometricState int newState) {
-        getIconController().updateState(mState, newState);
+        mFaceIconController.updateState(mState, newState);
 
         if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
                 (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 950bd83..e612fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -72,6 +72,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
@@ -123,6 +124,7 @@
     @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     @Nullable private final UdfpsHbmProvider mHbmProvider;
     @NonNull private final KeyguardBypassController mKeyguardBypassController;
+    @NonNull private final ConfigurationController mConfigurationController;
     @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
@@ -522,7 +524,8 @@
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull KeyguardBypassController keyguardBypassController,
             @NonNull DisplayManager displayManager,
-            @Main Handler mainHandler) {
+            @Main Handler mainHandler,
+            @NonNull ConfigurationController configurationController) {
         mContext = context;
         mExecution = execution;
         // TODO (b/185124905): inject main handler and vibrator once done prototyping
@@ -557,6 +560,7 @@
                 displayManager,
                 mainHandler);
         mKeyguardBypassController = keyguardBypassController;
+        mConfigurationController = configurationController;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -776,6 +780,7 @@
                         mDumpManager,
                         mKeyguardViewMediator,
                         mLockscreenShadeTransitionController,
+                        mConfigurationController,
                         this
                 );
             case IUdfpsOverlayController.REASON_AUTH_BP:
@@ -883,11 +888,16 @@
 
     private void onFingerDown(int x, int y, float minor, float major) {
         mExecution.assertIsMainThread();
-        mKeyguardBypassController.setUserHasDeviceEntryIntent(true);
         if (mView == null) {
             Log.w(TAG, "Null view in onFingerDown");
             return;
         }
+
+        if (mView.getAnimationViewController() instanceof UdfpsKeyguardViewController
+                && !mStatusBarStateController.isDozing()) {
+            mKeyguardBypassController.setUserHasDeviceEntryIntent(true);
+        }
+
         if (!mOnFingerDown) {
             playStartHaptic();
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index eb02aa0..d46426a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -22,9 +22,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -51,17 +49,14 @@
     private UdfpsDrawable mFingerprintDrawable; // placeholder
     private LottieAnimationView mAodFp;
     private LottieAnimationView mLockScreenFp;
-    private int mUdfpsBouncerColor;
-    private int mWallpaperTextColor;
     private int mStatusBarState;
 
     // used when highlighting fp icon:
     private int mTextColorPrimary;
     private ImageView mBgProtection;
     boolean mUdfpsRequested;
-    int mUdfpsRequestedColor;
 
-    private AnimatorSet mAnimatorSet;
+    private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
     private int mAlpha; // 0-255
 
     // AOD anti-burn-in offsets
@@ -72,8 +67,6 @@
     private float mBurnInProgress;
     private float mInterpolatedDarkAmount;
 
-    private ValueAnimator mHintAnimator;
-
     public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         mFingerprintDrawable = new UdfpsFpDrawable(context);
@@ -89,23 +82,15 @@
         super.onFinishInflate();
         mAodFp = findViewById(R.id.udfps_aod_fp);
         mLockScreenFp = findViewById(R.id.udfps_lockscreen_fp);
-
         mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
 
-        mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
-                R.attr.wallpaperTextColorAccent);
-        mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
-                android.R.attr.textColorPrimary);
+        updateColor();
 
-        // requires call to invalidate to update the color (see #updateColor)
+        // requires call to invalidate to update the color
         mLockScreenFp.addValueCallback(
                 new KeyPath("**"), LottieProperty.COLOR_FILTER,
-                frameInfo -> new PorterDuffColorFilter(getColor(), PorterDuff.Mode.SRC_ATOP)
+                frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, PorterDuff.Mode.SRC_ATOP)
         );
-        mUdfpsRequested = false;
-
-        mHintAnimator = ObjectAnimator.ofFloat(mLockScreenFp, "progress", 1f, 0f, 1f);
-        mHintAnimator.setDuration(4000);
     }
 
     @Override
@@ -148,13 +133,7 @@
     }
 
     void requestUdfps(boolean request, int color) {
-        if (request) {
-            mUdfpsRequestedColor = color;
-        } else {
-            mUdfpsRequestedColor = -1;
-        }
         mUdfpsRequested = request;
-        updateColor();
     }
 
     void setStatusBarState(int statusBarState) {
@@ -162,31 +141,10 @@
     }
 
     void updateColor() {
-        mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
-            R.attr.wallpaperTextColorAccent);
         mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
             android.R.attr.textColorPrimary);
-        mLockScreenFp.invalidate();
-        mBgProtection.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
-    }
-
-    private boolean showingUdfpsBouncer() {
-        return mBgProtection.getVisibility() == View.VISIBLE;
-    }
-
-
-    private int getColor() {
-        if (isUdfpsColorRequested()) {
-            return mUdfpsRequestedColor;
-        } else if (showingUdfpsBouncer()) {
-            return mUdfpsBouncerColor;
-        } else {
-            return mWallpaperTextColor;
-        }
-    }
-
-    private boolean isUdfpsColorRequested() {
-        return mUdfpsRequested && mUdfpsRequestedColor != -1;
+        mBgProtection.setImageDrawable(getContext().getDrawable(R.drawable.fingerprint_bg));
+        mLockScreenFp.invalidate(); // updated with a valueCallback
     }
 
     /**
@@ -200,7 +158,13 @@
     @Override
     protected int updateAlpha() {
         int alpha = super.updateAlpha();
-        mLockScreenFp.setImageAlpha(alpha);
+        mLockScreenFp.setAlpha(alpha / 255f);
+        if (mInterpolatedDarkAmount != 0f) {
+            mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
+        } else {
+            mBgProtection.setAlpha(alpha / 255f);
+        }
+
         return alpha;
     }
 
@@ -213,67 +177,29 @@
     }
 
     void onDozeAmountChanged(float linear, float eased) {
-        mHintAnimator.cancel();
         mInterpolatedDarkAmount = eased;
+        updateAlpha();
         updateBurnInOffsets();
     }
 
-    void animateHint() {
-        if (!isShadeLocked() && !mUdfpsRequested && mAlpha == 255
-                && mLockScreenFp.isVisibleToUser()) {
-            mHintAnimator.start();
-        }
-    }
-
     /**
      * Animates in the bg protection circle behind the fp icon to highlight the icon.
      */
-    void animateUdfpsBouncer(Runnable onEndAnimation) {
-        if (showingUdfpsBouncer() && mBgProtection.getAlpha() == 1f) {
-            // already fully highlighted, don't re-animate
+    void animateInUdfpsBouncer(Runnable onEndAnimation) {
+        if (mBackgroundInAnimator.isRunning()) {
+            // already animating in
             return;
         }
 
-        if (mAnimatorSet != null) {
-            mAnimatorSet.cancel();
-        }
-
-        mAnimatorSet = new AnimatorSet();
-        mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mAnimatorSet.setDuration(500);
-        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mBgProtection.setVisibility(View.VISIBLE);
-            }
-        });
-
-        ValueAnimator fpIconColorAnim;
-        if (isShadeLocked()) {
-            // set color and fade in since we weren't showing before
-            mUdfpsBouncerColor = mTextColorPrimary;
-            fpIconColorAnim = ValueAnimator.ofInt(0, 255);
-            fpIconColorAnim.addUpdateListener(valueAnimator ->
-                    mLockScreenFp.setImageAlpha((int) valueAnimator.getAnimatedValue()));
-        } else {
-            // update icon color
-            fpIconColorAnim = new ValueAnimator();
-            fpIconColorAnim.setIntValues(
-                    isUdfpsColorRequested() ? mUdfpsRequestedColor : mWallpaperTextColor,
-                    mTextColorPrimary);
-            fpIconColorAnim.setEvaluator(ArgbEvaluator.getInstance());
-            fpIconColorAnim.addUpdateListener(valueAnimator -> {
-                mUdfpsBouncerColor = (int) valueAnimator.getAnimatedValue();
-                updateColor();
-            });
-        }
-
-        mAnimatorSet.playTogether(
+        // fade in and scale up
+        mBackgroundInAnimator = new AnimatorSet();
+        mBackgroundInAnimator.playTogether(
                 ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
                 ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f),
-                fpIconColorAnim);
-        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f));
+        mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mBackgroundInAnimator.setDuration(500);
+        mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (onEndAnimation != null) {
@@ -281,66 +207,7 @@
                 }
             }
         });
-        mAnimatorSet.start();
-    }
-
-    /**
-     * Animates out the bg protection circle behind the fp icon to unhighlight the icon.
-     */
-    void animateAwayUdfpsBouncer(@Nullable Runnable onEndAnimation) {
-        if (!showingUdfpsBouncer()) {
-            // already hidden
-            return;
-        }
-
-        if (mAnimatorSet != null) {
-            mAnimatorSet.cancel();
-        }
-
-        ValueAnimator fpIconColorAnim;
-        if (isShadeLocked()) {
-            // fade out
-            mUdfpsBouncerColor = mTextColorPrimary;
-            fpIconColorAnim = ValueAnimator.ofInt(255, 0);
-            fpIconColorAnim.addUpdateListener(valueAnimator ->
-                    mLockScreenFp.setImageAlpha((int) valueAnimator.getAnimatedValue()));
-        } else {
-            // update icon color
-            fpIconColorAnim = new ValueAnimator();
-            fpIconColorAnim.setIntValues(
-                    mTextColorPrimary,
-                    isUdfpsColorRequested() ? mUdfpsRequestedColor : mWallpaperTextColor);
-            fpIconColorAnim.setEvaluator(ArgbEvaluator.getInstance());
-            fpIconColorAnim.addUpdateListener(valueAnimator -> {
-                mUdfpsBouncerColor = (int) valueAnimator.getAnimatedValue();
-                updateColor();
-            });
-        }
-
-        mAnimatorSet = new AnimatorSet();
-        mAnimatorSet.playTogether(
-                ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f),
-                fpIconColorAnim);
-        mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mAnimatorSet.setDuration(500);
-
-        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mBgProtection.setVisibility(View.GONE);
-                if (onEndAnimation != null) {
-                    onEndAnimation.run();
-                }
-            }
-        });
-
-        mAnimatorSet.start();
-    }
-
-    boolean isAnimating() {
-        return mAnimatorSet != null && mAnimatorSet.isRunning();
+        mBackgroundInAnimator.start();
     }
 
     private boolean isShadeLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 6435bdd..4896305 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -19,14 +19,11 @@
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 
 import android.annotation.NonNull;
-import android.hardware.biometrics.BiometricSourceType;
+import android.content.res.Configuration;
 import android.util.MathUtils;
 import android.view.MotionEvent;
 
-import androidx.annotation.Nullable;
-
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -36,6 +33,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.io.FileDescriptor;
@@ -44,27 +42,20 @@
 
 /**
  * Class that coordinates non-HBM animations during keyguard authentication.
- *
- * Highlights the udfps icon when:
- * - Face authentication has failed
- * - Face authentication has been run for > 2 seconds
  */
 public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
-    private static final long AFTER_FACE_AUTH_HINT_DELAY = 2000;
-
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final DelayableExecutor mExecutor;
     @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
     @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
+    @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final UdfpsController mUdfpsController;
 
-    @Nullable private Runnable mCancelDelayedHintRunnable;
     private boolean mShowingUdfpsBouncer;
     private boolean mUdfpsRequested;
     private boolean mQsExpanded;
     private boolean mFaceDetectRunning;
-    private boolean mHintShown;
     private int mStatusBarState;
     private float mTransitionToFullShadeProgress;
     private float mLastDozeAmount;
@@ -87,6 +78,7 @@
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardViewMediator keyguardViewMediator,
             @NonNull LockscreenShadeTransitionController transitionController,
+            @NonNull ConfigurationController configurationController,
             @NonNull UdfpsController udfpsController) {
         super(view, statusBarStateController, statusBar, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -94,6 +86,7 @@
         mExecutor = mainDelayableExecutor;
         mKeyguardViewMediator = keyguardViewMediator;
         mLockScreenShadeTransitionController = transitionController;
+        mConfigurationController = configurationController;
         mUdfpsController = udfpsController;
     }
 
@@ -105,10 +98,6 @@
     @Override
     protected void onViewAttached() {
         super.onViewAttached();
-        mHintShown = false;
-        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
-        updateFaceDetectRunning(mKeyguardUpdateMonitor.isFaceDetectionRunning());
-
         final float dozeAmount = mStatusBarStateController.getDozeAmount();
         mLastDozeAmount = dozeAmount;
         mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
@@ -120,6 +109,7 @@
         mQsExpanded = mKeyguardViewManager.isQsExpanded();
         mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
         mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
+        mConfigurationController.addCallback(mConfigurationListener);
         updateAlpha();
         updatePauseAuth();
 
@@ -130,20 +120,15 @@
     @Override
     protected void onViewDetached() {
         super.onViewDetached();
-        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         mFaceDetectRunning = false;
 
         mStatusBarStateController.removeCallback(mStateListener);
         mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+        mConfigurationController.removeCallback(mConfigurationListener);
         if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
-
-        if (mCancelDelayedHintRunnable != null) {
-            mCancelDelayedHintRunnable.run();
-            mCancelDelayedHintRunnable = null;
-        }
     }
 
     @Override
@@ -158,7 +143,6 @@
         pw.println("mAlpha=" + mView.getAlpha());
         pw.println("mUdfpsRequested=" + mUdfpsRequested);
         pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
-        pw.println("mView.mUdfpsRequestedColor=" + mView.mUdfpsRequestedColor);
     }
 
     /**
@@ -173,12 +157,17 @@
         mShowingUdfpsBouncer = show;
         updatePauseAuth();
         if (mShowingUdfpsBouncer) {
-            mView.animateUdfpsBouncer(() ->
-                    mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true));
+            if (mStatusBarState == StatusBarState.SHADE_LOCKED) {
+                mView.animateInUdfpsBouncer(null);
+            }
+
+            if (mKeyguardViewManager.isOccluded()) {
+                mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+            }
+
             mView.announceForAccessibility(mView.getContext().getString(
                     R.string.accessibility_fingerprint_bouncer));
         } else {
-            mView.animateAwayUdfpsBouncer(null);
             mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
         }
         return true;
@@ -238,36 +227,6 @@
         }
     }
 
-    private void cancelDelayedHint() {
-        if (mCancelDelayedHintRunnable != null) {
-            mCancelDelayedHintRunnable.run();
-            mCancelDelayedHintRunnable = null;
-        }
-    }
-
-    private void updateFaceDetectRunning(boolean running) {
-        if (mFaceDetectRunning == running) {
-            return;
-        }
-
-        // show udfps hint a few seconds after face auth started running
-        if (!mFaceDetectRunning && running && !mHintShown && mCancelDelayedHintRunnable == null) {
-            // Face detect started running, show udfps hint after a delay
-            mCancelDelayedHintRunnable = mExecutor.executeDelayed(() -> showHint(false),
-                    AFTER_FACE_AUTH_HINT_DELAY);
-        }
-
-        mFaceDetectRunning = running;
-    }
-
-    private void showHint(boolean forceShow) {
-        cancelDelayedHint();
-        if (!mHintShown || forceShow) {
-            mHintShown = true;
-            mView.animateHint();
-        }
-    }
-
     /**
      * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
      * transitioning yet, while 1.0f means we've fully dragged down.
@@ -307,39 +266,6 @@
         }
     };
 
-    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
-            new KeyguardUpdateMonitorCallback() {
-                public void onBiometricRunningStateChanged(boolean running,
-                        BiometricSourceType biometricSourceType) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        updateFaceDetectRunning(running);
-                    }
-                }
-
-                public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        // show udfps hint when face auth fails
-                        showHint(true);
-                    }
-                }
-
-                public void onBiometricError(int msgId, String errString,
-                        BiometricSourceType biometricSourceType) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        // show udfps hint when face auth fails
-                        showHint(true);
-                    }
-                }
-
-                public void onBiometricAuthenticated(int userId,
-                        BiometricSourceType biometricSourceType, boolean isStrongBiometric) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        // cancel delayed hint if face auth succeeded
-                        cancelDelayedHint();
-                    }
-                }
-            };
-
     private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor =
             new StatusBarKeyguardViewManager.AlternateAuthInterceptor() {
                 @Override
@@ -366,7 +292,7 @@
 
                 @Override
                 public boolean isAnimating() {
-                    return mView.isAnimating();
+                    return false;
                 }
 
                 @Override
@@ -407,4 +333,27 @@
                     pw.println(getTag());
                 }
             };
+
+    private final ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+                @Override
+                public void onUiModeChanged() {
+                    mView.updateColor();
+                }
+
+                @Override
+                public void onThemeChanged() {
+                    mView.updateColor();
+                }
+
+                @Override
+                public void onOverlayChanged() {
+                    mView.updateColor();
+                }
+
+                @Override
+                public void onConfigChanged(Configuration newConfig) {
+                    mView.updateColor();
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 239a77e..9cb9a36 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -39,6 +39,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
@@ -80,6 +81,7 @@
  */
 @Module(includes = {
         MediaModule.class,
+        PowerModule.class,
         QSModule.class
 })
 public abstract class SystemUIDefaultModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 556f956..46ab5f6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -41,7 +41,6 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.settings.dagger.SettingsModule;
@@ -102,7 +101,6 @@
             FalsingModule.class,
             LogModule.class,
             PeopleHubModule.class,
-            PowerModule.class,
             PluginModule.class,
             ScreenshotModule.class,
             SensorModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 23c4413..b2db86f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -115,6 +115,7 @@
         mSecureSettings = secureSettings;
         mCallback = callback;
         mProximitySensor = proximitySensor;
+        mProximitySensor.setTag(TAG);
         mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx();
         mListeningProxSensors = !mSelectivelyRegisterProxSensors;
         mScreenOffUdfpsEnabled =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c7c2590..84cb12c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -399,6 +399,12 @@
     private boolean mPendingLock;
 
     /**
+     * When starting to go away, flag a need to show the PIN lock so the keyguard can be brought
+     * back.
+     */
+    private boolean mPendingPinLock = false;
+
+    /**
      * Whether a power button gesture (such as double tap for camera) has been detected. This is
      * delivered directly from {@link KeyguardService}, immediately upon the gesture being detected.
      * This is used in {@link #onStartedWakingUp} to decide whether to execute the pending lock, or
@@ -472,6 +478,19 @@
     KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
 
         @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            synchronized (KeyguardViewMediator.this) {
+                if (!showing && mPendingPinLock) {
+                    Log.i(TAG, "PIN lock requested, starting keyguard");
+
+                    // Bring the keyguard back in order to show the PIN lock
+                    mPendingPinLock = false;
+                    doKeyguardLocked(null);
+                }
+            }
+        }
+
+        @Override
         public void onUserSwitching(int userId) {
             if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
             // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
@@ -591,6 +610,7 @@
                                     + "showing; need to show keyguard so user can enter sim pin");
                             doKeyguardLocked(null);
                         } else {
+                            mPendingPinLock = true;
                             resetStateLocked();
                         }
                     }
@@ -739,6 +759,9 @@
         @Override
         public void onBouncerVisiblityChanged(boolean shown) {
             synchronized (KeyguardViewMediator.this) {
+                if (shown) {
+                    mPendingPinLock = false;
+                }
                 adjustStatusBarLocked(shown, false);
             }
         }
@@ -2783,7 +2806,7 @@
         }
     }
 
-    private void setShowingLocked(boolean showing) {
+    void setShowingLocked(boolean showing) {
         setShowingLocked(showing, false /* forceCallbacks */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ce1066e..1bd3664 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -268,7 +268,7 @@
     }
 
     @Override
-    public void setExpansion(float expansion) {
+    public void setExpansion(float expansion, float proposedTranslation) {
         mLastExpansion = expansion;
         updateSelected();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e9b19e5..59e5eb8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -28,13 +28,17 @@
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.qs.customize.QSCustomizer;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * Wrapper view with background which contains {@link QSPanel} and {@link QuickStatusBarHeader}
  */
-public class QSContainerImpl extends FrameLayout {
+public class QSContainerImpl extends FrameLayout implements Dumpable {
 
     private final Point mSizePoint = new Point();
     private int mFancyClippingTop;
@@ -296,4 +300,11 @@
                 mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW);
         invalidate();
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(getClass().getSimpleName() + " updateClippingPath: top("
+                + mFancyClippingTop + ") bottom(" + mFancyClippingBottom  + ") mClippingEnabled("
+                + mClippingEnabled + ")");
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 04f692d..1c841ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -36,8 +36,10 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
@@ -131,6 +133,8 @@
      */
     private boolean mAnimateNextQsUpdate;
 
+    private DumpManager mDumpManager;
+
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
             InjectionInflationController injectionInflater, QSTileHost qsTileHost,
@@ -139,7 +143,7 @@
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
             KeyguardBypassController keyguardBypassController,
             QSFragmentComponent.Factory qsComponentFactory, FeatureFlags featureFlags,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager, DumpManager dumpManager) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mInjectionInflater = injectionInflater;
         mCommandQueue = commandQueue;
@@ -153,6 +157,7 @@
         mFalsingManager = falsingManager;
         mBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
+        mDumpManager = dumpManager;
     }
 
     @Override
@@ -197,6 +202,7 @@
         mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
         mQSContainerImplController.init();
         mContainer = mQSContainerImplController.getView();
+        mDumpManager.registerDumpable(mContainer.getClass().getName(), mContainer);
 
         mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager);
         mQSAnimator = qsFragmentComponent.getQSAnimator();
@@ -248,6 +254,7 @@
         mQSCustomizerController.setQs(null);
         mQsDetailDisplayer.setQsPanelController(null);
         mScrollListener = null;
+        mDumpManager.unregisterDumpable(mContainer.getClass().getName());
     }
 
     @Override
@@ -491,11 +498,13 @@
                             : headerTranslation);
         }
         int currentHeight = getView().getHeight();
-        mLastHeaderTranslation = headerTranslation;
-        if (expansion == mLastQSExpansion && mLastKeyguardAndExpanded == onKeyguardAndExpanded
-                && mLastViewHeight == currentHeight) {
+        if (expansion == mLastQSExpansion
+                && mLastKeyguardAndExpanded == onKeyguardAndExpanded
+                && mLastViewHeight == currentHeight
+                && mLastHeaderTranslation == headerTranslation) {
             return;
         }
+        mLastHeaderTranslation = headerTranslation;
         mLastQSExpansion = expansion;
         mLastKeyguardAndExpanded = onKeyguardAndExpanded;
         mLastViewHeight = currentHeight;
@@ -515,8 +524,8 @@
         }
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSPanelController.setRevealExpansion(expansion);
-        mQSPanelController.getTileLayout().setExpansion(expansion);
-        mQuickQSPanelController.getTileLayout().setExpansion(expansion);
+        mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
+        mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
         mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 425bdc2..cde80e66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -27,12 +27,15 @@
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.RemeasuringLinearLayout;
 import com.android.systemui.R;
@@ -386,19 +389,18 @@
                 if (mediaView != null) {
                     index = indexOfChild(mediaView);
                 }
+                if (mSecurityFooter.getParent() == this && indexOfChild(mSecurityFooter) < index) {
+                    // When we remove the securityFooter to rearrange, the index of media will go
+                    // down by one, so we correct it
+                    index--;
+                }
                 switchToParent(mSecurityFooter, this, index);
             }
         }
     }
 
     private void switchToParent(View child, ViewGroup parent, int index) {
-        ViewGroup currentParent = (ViewGroup) child.getParent();
-        if (currentParent != parent || currentParent.indexOfChild(child) != index) {
-            if (currentParent != null) {
-                currentParent.removeView(child);
-            }
-            parent.addView(child, index);
-        }
+        switchToParent(child, parent, index, getDumpableTag());
     }
 
     /** Call when orientation has changed and MediaHost needs to be adjusted. */
@@ -740,7 +742,7 @@
         void setListening(boolean listening, UiEventLogger uiEventLogger);
 
         /**
-         * Set the minimum number of rows to show
+         * Sets the minimum number of rows to show
          *
          * @param minRows the minimum.
          */
@@ -749,7 +751,7 @@
         }
 
         /**
-         * Set the max number of columns to show
+         * Sets the max number of columns to show
          *
          * @param maxColumns the maximum
          *
@@ -759,7 +761,10 @@
             return false;
         }
 
-        default void setExpansion(float expansion) {}
+        /**
+         * Sets the expansion value and proposedTranslation to panel.
+         */
+        default void setExpansion(float expansion, float proposedTranslation) {}
 
         int getNumVisibleTiles();
     }
@@ -767,4 +772,29 @@
     interface OnConfigurationChangedListener {
         void onConfigurationChange(Configuration newConfig);
     }
+
+    @VisibleForTesting
+    static void switchToParent(View child, ViewGroup parent, int index, String tag) {
+        if (parent == null) {
+            Log.w(tag, "Trying to move view to null parent",
+                    new IllegalStateException());
+            return;
+        }
+        ViewGroup currentParent = (ViewGroup) child.getParent();
+        if (currentParent != parent) {
+            if (currentParent != null) {
+                currentParent.removeView(child);
+            }
+            parent.addView(child, index);
+            return;
+        }
+        // Same parent, we are just changing indices
+        int currentIndex = parent.indexOfChild(child);
+        if (currentIndex == index) {
+            // We want to be in the same place. Nothing to do here
+            return;
+        }
+        parent.removeView(child);
+        parent.addView(child, index);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 985943b..c5bfe97 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -177,6 +177,8 @@
 
     static class QQSSideLabelTileLayout extends SideLabelTileLayout {
 
+        private boolean mLastSelected;
+
         QQSSideLabelTileLayout(Context context) {
             super(context, null);
             setClipChildren(false);
@@ -222,5 +224,29 @@
                 }
             }
         }
+
+        @Override
+        public void setExpansion(float expansion, float proposedTranslation) {
+            if (expansion > 0f && expansion < 1f) {
+                return;
+            }
+            // The cases we must set select for marquee when QQS/QS collapsed, and QS full expanded.
+            // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
+            // point we want them to be selected so the tiles will marquee (but not at other points
+            // of expansion.
+            boolean selected = (expansion == 1f || proposedTranslation < 0f);
+            if (mLastSelected == selected) {
+                return;
+            }
+            // We set it as not important while we change this, so setting each tile as selected
+            // will not cause them to announce themselves until the user has actually selected the
+            // item.
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            for (int i = 0; i < getChildCount(); i++) {
+                getChildAt(i).setSelected(selected);
+            }
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+            mLastSelected = selected;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index cd9db61..77906ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -95,6 +95,9 @@
     private List<String> mRssiIgnoredSlots;
     private boolean mIsSingleCarrier;
 
+    private boolean mHasCenterCutout;
+    private boolean mConfigShowBatteryEstimate;
+
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -203,9 +206,19 @@
         mPrivacyContainer.setLayoutParams(lp);
     }
 
+    private void updateBatteryMode() {
+        if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
+            mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
+        } else {
+            mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
+        }
+    }
+
     void updateResources() {
         Resources resources = mContext.getResources();
 
+        mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
+
         mRoundedCornerPadding = resources.getDimensionPixelSize(
                 R.dimen.rounded_corner_content_padding);
 
@@ -246,6 +259,7 @@
                 .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
         mHeaderQsPanel.setLayoutParams(qqsLP);
 
+        updateBatteryMode();
         updateHeadersPadding();
         updateAnimators();
     }
@@ -384,14 +398,14 @@
                 mClockIconsSeparatorLayoutParams.width = 0;
                 setSeparatorVisibility(false);
                 mShowClockIconsSeparator = false;
-                mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
+                mHasCenterCutout = false;
             } else {
                 datePrivacySeparatorLayoutParams.width = topCutout.width();
                 mDatePrivacySeparator.setVisibility(View.VISIBLE);
                 mClockIconsSeparatorLayoutParams.width = topCutout.width();
                 mShowClockIconsSeparator = true;
                 setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
-                mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
+                mHasCenterCutout = true;
             }
         }
         mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
@@ -399,6 +413,8 @@
         mCutOutPaddingLeft = padding.first;
         mCutOutPaddingRight = padding.second;
         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+
+        updateBatteryMode();
         updateHeadersPadding();
         return super.onApplyWindowInsets(insets);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 70685a6..222539d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -195,9 +195,6 @@
             // sibling methods to have special behavior for labelContainer.
             labelContainer.forceUnspecifiedMeasure = true
             secondaryLabel.alpha = 0f
-            // Do not marque in QQS
-            label.ellipsize = TextUtils.TruncateAt.END
-            secondaryLabel.ellipsize = TextUtils.TruncateAt.END
         }
         setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
         setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index ce6e469..93e5021 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -45,9 +45,12 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Background;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 /**
@@ -63,6 +66,8 @@
 
     private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
 
+    private final Executor mBgExecutor;
+
     /**
      * Represents the connection to a target window and provides a mechanism for requesting tiles.
      */
@@ -155,8 +160,10 @@
     private IBinder mHostWindowToken;
 
     @Inject
-    public ScrollCaptureClient(@UiContext Context context, IWindowManager windowManagerService) {
+    public ScrollCaptureClient(IWindowManager windowManagerService,
+            @Background Executor bgExecutor, @UiContext Context context) {
         requireNonNull(context.getDisplay(), "context must be associated with a Display!");
+        mBgExecutor = bgExecutor;
         mWindowManagerService = windowManagerService;
     }
 
@@ -220,21 +227,25 @@
                 return "";
             }
             SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(),
-                    response.getBoundsInWindow(), maxPages);
+                    response.getBoundsInWindow(), maxPages, mBgExecutor);
             session.start(completer);
             return "IScrollCaptureCallbacks#onCaptureStarted";
         });
     }
 
     private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session,
-            IBinder.DeathRecipient {
+            IBinder.DeathRecipient, ImageReader.OnImageAvailableListener {
 
         private IScrollCaptureConnection mConnection;
+        private final Executor mBgExecutor;
+        private final Object mLock = new Object();
 
         private ImageReader mReader;
         private final int mTileHeight;
         private final int mTileWidth;
         private Rect mRequestRect;
+        private Rect mCapturedArea;
+        private Image mCapturedImage;
         private boolean mStarted;
         private final int mTargetHeight;
 
@@ -247,7 +258,8 @@
         private Completer<Void> mEndCompleter;
 
         private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds,
-                Rect boundsInWindow, float maxPages) throws RemoteException {
+                Rect boundsInWindow, float maxPages, Executor bgExecutor)
+                throws RemoteException {
             mConnection = requireNonNull(connection);
             mConnection.asBinder().linkToDeath(SessionWrapper.this, 0);
             mWindowBounds = requireNonNull(windowBounds);
@@ -259,7 +271,7 @@
             mTileWidth = mBoundsInWindow.width();
             mTileHeight = pxPerTile / mBoundsInWindow.width();
             mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);
-
+            mBgExecutor = bgExecutor;
             if (DEBUG_SCROLL) {
                 Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
                 Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
@@ -289,6 +301,7 @@
             mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
                     MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
             mStartCompleter = completer;
+            mReader.setOnImageAvailableListenerWithExecutor(this, mBgExecutor);
             try {
                 mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
                 completer.addCancellationListener(() -> {
@@ -339,9 +352,34 @@
 
         @BinderThread
         @Override
-        public void onImageRequestCompleted(int flags, Rect contentArea) {
-            Image image = mReader.acquireLatestImage();
-            mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea));
+        public void onImageRequestCompleted(int flagsUnused, Rect contentArea) {
+            synchronized (mLock) {
+                mCapturedArea = contentArea;
+                if (mCapturedImage != null || (mCapturedArea == null || mCapturedArea.isEmpty())) {
+                    completeCaptureRequest();
+                }
+            }
+        }
+
+        /** @see ImageReader.OnImageAvailableListener */
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            synchronized (mLock) {
+                mCapturedImage = mReader.acquireLatestImage();
+                if (mCapturedArea != null) {
+                    completeCaptureRequest();
+                }
+            }
+        }
+
+        /** Produces a result for the caller as soon as both asynchronous results are received. */
+        private void completeCaptureRequest() {
+            CaptureResult result =
+                    new CaptureResult(mCapturedImage, mRequestRect, mCapturedArea);
+            mCapturedImage = null;
+            mRequestRect = null;
+            mCapturedArea = null;
+            mTileRequestCompleter.set(result);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 39d6c4f..1ad253e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -354,10 +354,9 @@
                 convertGammaToLinearFloat(value, minBacklight, maxBacklight),
                 maxBacklight);
         if (stopTracking) {
-            // Log brightness as a value between 0-1000 directly correlated to brightnesses 0-1.0
+            // TODO(brightnessfloat): change to use float value instead.
             MetricsLogger.action(mContext, metric,
-                    Math.round(MathUtils.constrainedMap(0, 1000, PowerManager.BRIGHTNESS_MIN,
-                        PowerManager.BRIGHTNESS_MAX, valFloat)));
+                    BrightnessSynchronizer.brightnessFloatToInt(valFloat));
 
         }
         setBrightness(valFloat);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 92922b6..503b5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -821,6 +821,20 @@
         }
     }
 
+    private void showTryFingerprintMsg() {
+        if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+            // if udfps available, there will always be a tappable affordance to unlock
+            // For example, the lock icon
+            if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
+                showTransientIndication(R.string.keyguard_unlock_press);
+            } else {
+                showTransientIndication(R.string.keyguard_face_failed_use_fp);
+            }
+        } else {
+            showTransientIndication(R.string.keyguard_try_fingerprint);
+        }
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardIndicationController:");
         pw.println("  mInitialTextColorState: " + mInitialTextColorState);
@@ -891,7 +905,7 @@
                 return;
             }
 
-            boolean showSwipeToUnlock =
+            boolean showActionToUnlock =
                     msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
@@ -899,13 +913,11 @@
             } else if (mKeyguardUpdateMonitor.isScreenOn()) {
                 if (biometricSourceType == BiometricSourceType.FACE
                         && shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
-                    // suggest trying fingerprint
-                    showTransientIndication(R.string.keyguard_try_fingerprint);
+                    showTryFingerprintMsg();
                     return;
                 }
-                showTransientIndication(helpString, false /* isError */, showSwipeToUnlock);
-            }
-            if (showSwipeToUnlock) {
+                showTransientIndication(helpString, false /* isError */, showActionToUnlock);
+            } else if (showActionToUnlock) {
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
                         TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             }
@@ -921,8 +933,7 @@
                     && shouldSuppressFaceMsgAndShowTryFingerprintMsg()
                     && !mStatusBarKeyguardViewManager.isBouncerShowing()
                     && mKeyguardUpdateMonitor.isScreenOn()) {
-                // suggest trying fingerprint
-                showTransientIndication(R.string.keyguard_try_fingerprint);
+                showTryFingerprintMsg();
                 return;
             }
             if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -931,11 +942,10 @@
                 if (!mStatusBarKeyguardViewManager.isBouncerShowing()
                         && mKeyguardUpdateMonitor.isUdfpsEnrolled()
                         && mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
-                    // suggest trying fingerprint
-                    showTransientIndication(R.string.keyguard_try_fingerprint);
+                    showTryFingerprintMsg();
                 } else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
                     mStatusBarKeyguardViewManager.showBouncerMessage(
-                            mContext.getResources().getString(R.string.keyguard_try_fingerprint),
+                            mContext.getResources().getString(R.string.keyguard_unlock_press),
                             mInitialTextColorState
                     );
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 1b4ae9e..2c810c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -157,15 +157,13 @@
         // After the shelf has updated its yTranslation, explicitly set alpha=0 for view below shelf
         // to skip rendering them in the hardware layer. We do not set them invisible because that
         // runs invalidate & onDraw when these views return onscreen, which is more expensive.
+        if (shelf.getViewState().hidden) {
+            // When the shelf is hidden, it won't clip views, so we don't hide rows
+            return;
+        }
         final float shelfTop = shelf.getViewState().yTranslation;
 
         for (ExpandableView view : algorithmState.visibleChildren) {
-            if (view instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                if (row.isHeadsUp() || row.isHeadsUpAnimatingAway()) {
-                    continue;
-                }
-            }
             final float viewTop = view.getViewState().yTranslation;
             if (viewTop >= shelfTop) {
                 view.getViewState().alpha = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6802472..5a6db21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -231,7 +231,9 @@
      * possible if AOD isn't even enabled or if the flag is disabled.
      */
     public boolean canControlUnlockedScreenOff() {
-        return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
+        return getAlwaysOn()
+                && mFeatureFlags.useNewLockscreenAnimations()
+                && !getDisplayNeedsBlanking();
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index ec66971..3a4a819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -222,6 +222,7 @@
         pw.println("  launchingAffordance: $launchingAffordance")
         pw.println("  qSExpanded: $qSExpanded")
         pw.println("  hasFaceFeature: $hasFaceFeature")
+        pw.println("  userHasDeviceEntryIntent: $userHasDeviceEntryIntent")
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index ad4d450..15e0716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -631,6 +631,9 @@
     private int mScreenCornerRadius;
     private boolean mQSAnimatingHiddenFromCollapsed;
 
+    private int mQsClipTop;
+    private int mQsClipBottom;
+    private boolean mQsVisible;
     private final ContentResolver mContentResolver;
 
     private final Executor mUiExecutor;
@@ -2402,9 +2405,12 @@
             mQsTranslationForFullShadeTransition = qsTranslation;
             updateQsFrameTranslation();
             float currentTranslation = mQsFrame.getTranslationY();
-            mQs.setFancyClipping((
-                    int) (top - currentTranslation),
-                    (int) (bottom - currentTranslation),
+            mQsClipTop = (int) (top - currentTranslation);
+            mQsClipBottom = (int) (bottom - currentTranslation);
+            mQsVisible = qsVisible;
+            mQs.setFancyClipping(
+                    mQsClipTop,
+                    mQsClipBottom,
                     radius, qsVisible
                     && !mShouldUseSplitNotificationShade);
         }
@@ -3242,7 +3248,10 @@
         switch (mBarState) {
             case KEYGUARD:
                 if (!mDozingOnDown) {
-                    if (mKeyguardBypassController.getBypassEnabled()) {
+                    if (mUpdateMonitor.isFaceEnrolled()
+                            && !mUpdateMonitor.isFaceDetectionRunning()
+                            && !mUpdateMonitor.getUserCanSkipBouncer(
+                                    KeyguardUpdateMonitor.getCurrentUser())) {
                         mUpdateMonitor.requestFaceAuth(true);
                     } else {
                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
@@ -3733,7 +3742,10 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         super.dump(fd, pw, args);
-        pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect());
+        pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect()
+                + " applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom
+                + ") qsVisible(" + mQsVisible
+        );
         if (mKeyguardStatusBar != null) {
             mKeyguardStatusBar.dump(fd, pw, args);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5e105bb6..246810a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -296,8 +296,7 @@
 
         if (mKeyguardPreferredRefreshRate > 0) {
             boolean onKeyguard = state.mStatusBarState == StatusBarState.KEYGUARD
-                    && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway
-                    && !state.mDozing;
+                    && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
             if (onKeyguard
                     && mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
                 mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index df889f2..3525100 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -39,6 +39,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
@@ -81,6 +82,7 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
+            PowerModule.class,
             QSModule.class
         },
         subcomponents = {
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index b38fc77..90e022a5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -290,15 +290,18 @@
             return;
         }
 
-        if (!mSecondaryThresholdSensor.isLoaded()) {
+
+        if (!mSecondaryThresholdSensor.isLoaded()) {  // No secondary
             logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
             onSensorEvent(event);
-        } else if (event.getBelow()) {
+        } else if (event.getBelow()) {  // Covered? Check secondary.
             logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary.");
             if (mCancelSecondaryRunnable != null) {
                 mCancelSecondaryRunnable.run();
             }
             mSecondaryThresholdSensor.resume();
+        } else {  // Uncovered. Report immediately.
+            onSensorEvent(event);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 3320852..60b92ef 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -2248,6 +2248,11 @@
 
         @Override
         public void onClick(View view) {
+            // If the ringer drawer isn't open, don't let anything in it be clicked.
+            if (!mIsRingerDrawerOpen) {
+                return;
+            }
+
             setRingerMode(mClickedRingerMode);
 
             mRingerDrawerIconAnimatingSelected = getDrawerIconViewForMode(mClickedRingerMode);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index 5cd7810..090cf9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -50,7 +49,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -78,14 +76,16 @@
     @Mock private TextView mIndicatorView;
     @Mock private ImageView mIconView;
     @Mock private View mIconHolderView;
-    @Mock private AuthBiometricFaceView.IconController mIconController;
+    @Mock private AuthBiometricFaceView.IconController mFaceIconController;
+    @Mock private AuthBiometricFaceToFingerprintView.UdfpsIconController mUdfpsIconController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
         mFaceToFpView = new TestableView(mContext);
-        mFaceToFpView.mIconController = mIconController;
+        mFaceToFpView.mFaceIconController = mFaceIconController;
+        mFaceToFpView.mUdfpsIconController = mUdfpsIconController;
         mFaceToFpView.setCallback(mCallback);
 
         mFaceToFpView.mNegativeButton = mNegativeButton;
@@ -99,20 +99,23 @@
     @Test
     public void testStateUpdated_whenDialogAnimatedIn() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+        verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
     }
 
     @Test
     public void testIconUpdatesState_whenDialogStateUpdated() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+        verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
 
         mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
-        verify(mFaceToFpView.mIconController).updateState(
+        verify(mFaceToFpView.mFaceIconController).updateState(
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED));
+        verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
 
         assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState);
     }
@@ -120,21 +123,22 @@
     @Test
     public void testStateUpdated_whenSwitchToFingerprint() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
 
         mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
-        mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
 
-        InOrder order = inOrder(mFaceToFpView.mIconController);
-        order.verify(mFaceToFpView.mIconController).updateState(
+        verify(mFaceToFpView.mFaceIconController).deactivate();
+        verify(mFaceToFpView.mUdfpsIconController).updateState(
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
                 eq(AuthBiometricFaceToFingerprintView.STATE_ERROR));
-        order.verify(mFaceToFpView.mIconController).updateState(
+        verify(mConfirmButton).setVisibility(eq(View.GONE));
+
+        mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
+
+        verify(mFaceToFpView.mUdfpsIconController).updateState(
                 eq(AuthBiometricFaceToFingerprintView.STATE_ERROR),
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
-
-        verify(mConfirmButton).setVisibility(eq(View.GONE));
     }
 
     @Test
@@ -172,7 +176,10 @@
                 eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
         verify(mCallback).onAction(
                 eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
-        assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+
+        // First we enter the error state, since we need to show the error animation/text. The
+        // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
+        assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
     }
 
     @Test
@@ -185,13 +192,16 @@
                 eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
         verify(mCallback).onAction(
                 eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
-        assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+
+        // First we enter the error state, since we need to show the error animation/text. The
+        // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
+        assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
     }
 
     @Test
     public void testFingerprintOnlyStartsOnFirstError() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
 
         mFaceToFpView.onDialogAnimatedIn();
@@ -260,11 +270,6 @@
         protected int getDelayAfterAuthenticatedDurationMs() {
             return 0;
         }
-
-        @Override
-        protected IconController createUdfpsIconController() {
-            return AuthBiometricFaceToFingerprintViewTest.this.mIconController;
-        }
     }
 
     private class MockInjector extends AuthBiometricView.Injector {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
index 043bd5c..b93381d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
@@ -62,7 +62,7 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mFaceView = new TestableFaceView(mContext);
-        mFaceView.mIconController = mock(TestableFaceView.TestableIconController.class);
+        mFaceView.mFaceIconController = mock(TestableFaceView.TestableIconController.class);
         mFaceView.setCallback(mCallback);
 
         mFaceView.mNegativeButton = mNegativeButton;
@@ -78,18 +78,18 @@
     @Test
     public void testStateUpdated_whenDialogAnimatedIn() {
         mFaceView.onDialogAnimatedIn();
-        verify(mFaceView.mIconController)
+        verify(mFaceView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
     }
 
     @Test
     public void testIconUpdatesState_whenDialogStateUpdated() {
         mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATING);
-        verify(mFaceView.mIconController)
+        verify(mFaceView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
 
         mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
-        verify(mFaceView.mIconController).updateState(
+        verify(mFaceView.mFaceIconController).updateState(
                 eq(AuthBiometricFaceView.STATE_AUTHENTICATING),
                 eq(AuthBiometricFaceView.STATE_AUTHENTICATED));
     }
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 ca11451..2120b0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -62,6 +63,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.Execution;
 import com.android.systemui.util.concurrency.FakeExecution;
@@ -143,6 +145,8 @@
     private DisplayManager mDisplayManager;
     @Mock
     private Handler mHandler;
+    @Mock
+    private ConfigurationController mConfigurationController;
 
     private FakeExecutor mFgExecutor;
 
@@ -150,6 +154,10 @@
     @Mock
     private UdfpsView mUdfpsView;
     @Mock
+    private UdfpsEnrollView mEnrollView;
+    @Mock
+    private UdfpsKeyguardView mKeyguardView;
+    @Mock
     private UdfpsKeyguardViewController mUdfpsKeyguardViewController;
     @Mock
     private TypedArray mBrightnessValues;
@@ -171,7 +179,13 @@
         setUpResources();
         mExecution = new FakeExecution();
 
-        when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
+        when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
+                .thenReturn(mUdfpsView);
+        when(mLayoutInflater.inflate(R.layout.udfps_enroll_view, null))
+                .thenReturn(mEnrollView); // for showOverlay REASON_ENROLL_ENROLLING
+        when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null))
+                .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
+        when(mEnrollView.getContext()).thenReturn(mContext);
         final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
 
         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -214,7 +228,8 @@
                 mKeyguardStateController,
                 mKeyguardBypassController,
                 mDisplayManager,
-                mHandler);
+                mHandler,
+                mConfigurationController);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -264,6 +279,75 @@
     }
 
     @Test
+    public void onActionMove_dozing_setDeviceEntryIntent() throws RemoteException {
+        // GIVEN the current animation is UdfpsKeyguardViewController and device IS dozing
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
+        when(mStatusBarStateController.isDozing()).thenReturn(true);
+
+        // GIVEN that the overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN device entry intent is never to true b/c device was dozing on touch
+        verify(mKeyguardBypassController, never()).setUserHasDeviceEntryIntent(true);
+    }
+
+    @Test
+    public void onActionMove_onKeyguard_setDeviceEntryIntent() throws RemoteException {
+        // GIVEN the current animation is UdfpsKeyguardViewController and device isn't dozing
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+
+        // GIVEN that the overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN device entry intent is set to true
+        verify(mKeyguardBypassController).setUserHasDeviceEntryIntent(true);
+    }
+
+    @Test
+    public void onActionMove_onEnrollment_neverSetDeviceEntryIntent() throws RemoteException {
+        // GIVEN the current animation is UdfpsEnrollViewController
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mUdfpsView.getAnimationViewController()).thenReturn(
+                mock(UdfpsEnrollViewController.class));
+
+        // GIVEN that the overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN device entry intent is never set
+        verify(mKeyguardBypassController, never()).setUserHasDeviceEntryIntent(anyBoolean());
+    }
+
+    @Test
     public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
         // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index f62587c..0c03a51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import org.junit.Before;
@@ -76,6 +77,8 @@
     @Mock
     private KeyguardViewMediator mKeyguardViewMediator;
     @Mock
+    private ConfigurationController mConfigurationController;
+    @Mock
     private UdfpsController mUdfpsController;
 
     private UdfpsKeyguardViewController mController;
@@ -110,6 +113,7 @@
                 mDumpManager,
                 mKeyguardViewMediator,
                 mLockscreenShadeTransitionController,
+                mConfigurationController,
                 mUdfpsController);
     }
 
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 1dacc62..ad08780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -33,8 +34,9 @@
 import android.app.trust.TrustManager;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
+import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -65,7 +67,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class KeyguardViewMediatorTest extends SysuiTestCase {
     private KeyguardViewMediator mViewMediator;
@@ -124,6 +126,7 @@
                 mUnlockedScreenOffAnimationController,
                 () -> mNotificationShadeDepthController);
         mViewMediator.start();
+        mViewMediator.onSystemReady();
     }
 
     @Test
@@ -160,4 +163,27 @@
         mViewMediator.onDozeAmountChanged(1f, 1f);
         assertFalse(mViewMediator.isAnimatingScreenOff());
     }
+
+    @Test
+    public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway() {
+        // When showing and provisioned
+        when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
+        mViewMediator.setShowingLocked(true);
+
+        // and a SIM becomes locked and requires a PIN
+        mViewMediator.mUpdateCallback.onSimStateChanged(
+                1 /* subId */,
+                0 /* slotId */,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED);
+
+        // and the keyguard goes away
+        mViewMediator.setShowingLocked(false);
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
+
+        TestableLooper.get(this).processAllMessages();
+
+        // then make sure it comes back
+        verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index c1a7bc5..3ee3e55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -190,6 +190,7 @@
                 mBypassController,
                 mQsComponentFactory,
                 mFeatureFlags,
-                mFalsingManager);
+                mFalsingManager,
+                mock(DumpManager.class));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
new file mode 100644
index 0000000..56f2905
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import com.google.common.truth.Truth.assertThat
+
+import androidx.test.filters.SmallTest
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.children
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSPanelSwitchToParentTest : SysuiTestCase() {
+
+    private lateinit var parent1: FrameLayout
+    private lateinit var parent2: FrameLayout
+
+    private lateinit var movingView: View
+
+    private lateinit var view1A: View
+    private lateinit var view1B: View
+    private lateinit var view1C: View
+
+    private lateinit var view2A: View
+    private lateinit var view2B: View
+    private lateinit var view2C: View
+
+    @Before
+    fun setUp() {
+        parent1 = FrameLayout(mContext)
+        parent2 = FrameLayout(mContext)
+
+        movingView = View(mContext)
+
+        view1A = View(mContext)
+        parent1.addView(view1A)
+        view1B = View(mContext)
+        parent1.addView(view1B)
+        view1C = View(mContext)
+        parent1.addView(view1C)
+
+        view2A = View(mContext)
+        parent2.addView(view2A)
+        view2B = View(mContext)
+        parent2.addView(view2B)
+        view2C = View(mContext)
+        parent2.addView(view2C)
+    }
+
+    @Test
+    fun testNullTargetNoInteractions() {
+        QSPanel.switchToParent(movingView, null, -1, "")
+
+        assertThat(movingView.parent).isNull()
+    }
+
+    @Test
+    fun testMoveToEndNoParent() {
+        QSPanel.switchToParent(movingView, parent2, -1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, view2C, movingView
+        )
+    }
+
+    @Test
+    fun testMoveToEndDifferentParent() {
+        parent1.addView(movingView, 0)
+
+        QSPanel.switchToParent(movingView, parent2, -1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, view2C, movingView
+        )
+    }
+
+    @Test
+    fun testMoveToEndSameParent() {
+        parent2.addView(movingView, 0)
+
+        QSPanel.switchToParent(movingView, parent2, -1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, view2C, movingView
+        )
+    }
+
+    @Test
+    fun testMoveToMiddleFromNoParent() {
+        QSPanel.switchToParent(movingView, parent2, 1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, movingView, view2B, view2C
+        )
+    }
+
+    @Test
+    fun testMoveToMiddleDifferentParent() {
+        parent1.addView(movingView, 1)
+
+        QSPanel.switchToParent(movingView, parent2, 2, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, movingView, view2C
+        )
+    }
+
+    @Test
+    fun testMoveToMiddleSameParent() {
+        parent2.addView(movingView, 0)
+
+        QSPanel.switchToParent(movingView, parent2, 1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, movingView, view2B, view2C
+        )
+    }
+
+    private val ViewGroup.childrenList: List<View>
+        get() = children.toList()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 4f88599..16d4ddd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.qs;
 
+import static junit.framework.Assert.assertEquals;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -21,19 +23,20 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Configuration;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.util.animation.UniqueObjectHostView;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,29 +59,28 @@
     private QSTileImpl dndTile;
     @Mock
     private QSPanelControllerBase.TileRecord mDndTileRecord;
-    @Mock
-    private QSLogger mQSLogger;
     private ViewGroup mParentView;
     @Mock
     private QSDetail.Callback mCallback;
     @Mock
     private QSTileView mQSTileView;
+
+    private UniqueObjectHostView mMediaView;
+    private View mSecurityFooter;
     @Mock
-    private ActivityStarter mActivityStarter;
+    private FrameLayout mHeaderContainer;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mTestableLooper = TestableLooper.get(this);
 
-//        // Dependencies for QSSecurityFooter
-//        mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
-//        mDependency.injectMockDependency(SecurityController.class);
-//        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-//        mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
         mDndTileRecord.tile = dndTile;
         mDndTileRecord.tileView = mQSTileView;
 
+        mMediaView = new UniqueObjectHostView(mContext);
+        mSecurityFooter = new View(mContext);
+
         mTestableLooper.runWithLooper(() -> {
             mQsPanel = new QSPanel(mContext, null);
             mQsPanel.initialize();
@@ -92,6 +94,7 @@
             when(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView);
             mQsPanel.addTile(mDndTileRecord);
             mQsPanel.setCallback(mCallback);
+            mQsPanel.setHeaderContainer(mHeaderContainer);
         });
     }
 
@@ -112,4 +115,62 @@
 
         verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
     }
+
+    @Test
+    public void testSecurityFooterAtEndNoMedia_portrait() {
+        mTestableLooper.processAllMessages();
+
+        mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+
+        mQsPanel.setSecurityFooter(mSecurityFooter);
+
+        int children = mQsPanel.getChildCount();
+        assertEquals(children - 1, mQsPanel.indexOfChild(mSecurityFooter));
+    }
+
+    @Test
+    public void testSecurityFooterRightBeforeMedia_portrait() {
+        mTestableLooper.processAllMessages();
+
+        mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+
+        mQsPanel.addView(mMediaView);
+
+        mQsPanel.setSecurityFooter(mSecurityFooter);
+
+        int securityFooterIndex = mQsPanel.indexOfChild(mSecurityFooter);
+        int mediaIndex = mQsPanel.indexOfChild(mMediaView);
+
+        assertEquals(mediaIndex - 1, securityFooterIndex);
+    }
+
+    @Test
+    public void testSecurityFooterRightBeforeMedia_portrait_configChange() {
+        mTestableLooper.processAllMessages();
+
+        mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+
+        mQsPanel.addView(mMediaView);
+
+        mQsPanel.setSecurityFooter(mSecurityFooter);
+
+        mQsPanel.onConfigurationChanged(mContext.getResources().getConfiguration());
+
+        int securityFooterIndex = mQsPanel.indexOfChild(mSecurityFooter);
+        int mediaIndex = mQsPanel.indexOfChild(mMediaView);
+
+        assertEquals(mediaIndex - 1, securityFooterIndex);
+    }
+
+    @Test
+    public void testSecurityFooterInHeader_landscape() {
+        mTestableLooper.processAllMessages();
+
+        mContext.getResources().getConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
+
+        mQsPanel.setSecurityFooter(mSecurityFooter);
+
+        verify(mHeaderContainer).addView(mSecurityFooter, 0);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 3a4bc69..670a130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 
-import android.content.Context;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
@@ -36,7 +35,6 @@
 import android.view.ScrollCaptureResponse;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
@@ -83,7 +81,7 @@
                 /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class));
 
         // Create client
-        ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
+        ScrollCaptureClient client = new ScrollCaptureClient(mWm, Runnable::run, mContext);
 
         // Request scroll capture
         ListenableFuture<ScrollCaptureResponse> requestFuture =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
index cc2afe2..a34c598 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -60,6 +61,68 @@
     }
 
     @Test
+    public void testInitiallyAbovePrimary() {
+
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorPrimary.triggerEvent(false, 0);
+        assertNotNull(listener.mLastEvent);
+        assertFalse(listener.mLastEvent.getBelow());
+        assertEquals(1, listener.mCallCount);
+    }
+
+    @Test
+    public void testInitiallyBelowPrimaryAboveSecondary() {
+
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorPrimary.triggerEvent(true, 0);
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorSecondary.triggerEvent(false, 1);
+        assertNotNull(listener.mLastEvent);
+        assertFalse(listener.mLastEvent.getBelow());
+        assertEquals(1, listener.mCallCount);
+    }
+
+    @Test
+    public void testInitiallyBelowPrimaryAndSecondary() {
+
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorPrimary.triggerEvent(true, 0);
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorSecondary.triggerEvent(true, 1);
+        assertNotNull(listener.mLastEvent);
+        assertTrue(listener.mLastEvent.getBelow());
+        assertEquals(1, listener.mCallCount);
+    }
+
+    @Test
     public void testPrimaryBelowDoesNotInvokeSecondary() {
         TestableListener listener = new TestableListener();
 
@@ -74,8 +137,6 @@
         mThresholdSensorPrimary.triggerEvent(false, 0);
         assertFalse(mThresholdSensorPrimary.isPaused());
         assertTrue(mThresholdSensorSecondary.isPaused());
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
     }
 
     @Test
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 85877dd3..df269d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -96,6 +96,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -346,6 +347,8 @@
      */
     private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
 
+    private final AccessibilityManager mAccessibilityManager;
+
     void onSwitchInputMethodLocked() {
         // One caveat is that for the case where the focus is on a field for which regular autofill
         // returns null, and augmented autofill is triggered,  and then the user switches the input
@@ -458,9 +461,14 @@
                     return;
                 }
 
-                mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
-                        mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
-                        mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+                // If a11y touch exploration is enabled, then we do not send an inline fill request
+                // to the regular af service, because dropdown UI is easier to use.
+                if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+                    mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+                            mPendingFillRequest.getFillContexts(),
+                            mPendingFillRequest.getClientState(),
+                            mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+                }
             }
 
             mRemoteFillService.onFillRequest(mPendingFillRequest);
@@ -889,6 +897,7 @@
         mRemoteFillService = serviceComponentName == null ? null
                 : new RemoteFillService(context, serviceComponentName, userId, this,
                         bindInstantServiceAllowed);
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mActivityToken = activityToken;
         mHasCallback = hasCallback;
         mUiLatencyHistory = uiLatencyHistory;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 85ff2be..b7c61a0 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -2505,6 +2505,16 @@
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions());
     }
 
+    private boolean isBleState(int state) {
+        switch (state) {
+            case BluetoothAdapter.STATE_BLE_ON:
+            case BluetoothAdapter.STATE_BLE_TURNING_ON:
+            case BluetoothAdapter.STATE_BLE_TURNING_OFF:
+                return true;
+        }
+        return false;
+    }
+
     @RequiresPermission(allOf = {
             android.Manifest.permission.BLUETOOTH_CONNECT,
             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
@@ -2527,8 +2537,15 @@
                 sendBluetoothServiceDownCallback();
                 unbindAndFinish();
                 sendBleStateChanged(prevState, newState);
-                // Don't broadcast as it has already been broadcast before
-                isStandardBroadcast = false;
+
+                /* Currently, the OFF intent is broadcasted externally only when we transition
+                 * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state,
+                 * we are guaranteed that the OFF intent has been broadcasted earlier and we
+                 * can safely skip it.
+                 * Conversely, if the previous state is not a BLE state, it indicates that some
+                 * sort of crash has occurred, moving us directly to STATE_OFF without ever
+                 * passing through BLE_ON. We should broadcast the OFF intent in this case. */
+                isStandardBroadcast = !isBleState(prevState);
 
             } else if (!intermediate_off) {
                 // connect to GattService
@@ -2581,6 +2598,11 @@
                 // Show prevState of BLE_ON as OFF to standard users
                 prevState = BluetoothAdapter.STATE_OFF;
             }
+            if (DBG) {
+                Slog.d(TAG,
+                        "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
+                                + BluetoothAdapter.nameForState(newState));
+            }
             Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
             intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
             intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 019e4ea..40e63da 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -21,8 +21,6 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
 import static android.app.ActivityManager.RunningServiceInfo;
 import static android.app.ActivityManager.RunningTaskInfo;
-import static android.app.ActivityManager.getCurrentUser;
-import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
@@ -383,17 +381,9 @@
 
             int sensor;
             if (result == MODE_IGNORED) {
-                if (code == OP_RECORD_AUDIO) {
+                if (code == OP_RECORD_AUDIO || code == OP_PHONE_CALL_MICROPHONE) {
                     sensor = MICROPHONE;
-                } else if (code == OP_CAMERA) {
-                    sensor = CAMERA;
-                } else {
-                    return;
-                }
-            } else if (result == MODE_ALLOWED) {
-                if (code == OP_PHONE_CALL_MICROPHONE) {
-                    sensor = MICROPHONE;
-                } else if (code == OP_PHONE_CALL_CAMERA) {
+                } else if (code == OP_CAMERA || code == OP_PHONE_CALL_CAMERA) {
                     sensor = CAMERA;
                 } else {
                     return;
@@ -718,6 +708,9 @@
         public void setIndividualSensorPrivacy(@UserIdInt int userId,
                 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
             enforceManageSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
                 return;
             }
@@ -843,6 +836,9 @@
         public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId,
                 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
             enforceManageSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             int parentId = mUserManagerInternal.getProfileParentId(userId);
             forAllUsers(userId2 -> {
                 if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
@@ -896,6 +892,9 @@
         @Override
         public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
             enforceObserveSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             synchronized (mLock) {
                 return isIndividualSensorPrivacyEnabledLocked(userId, sensor);
             }
@@ -1213,6 +1212,9 @@
         public void suppressIndividualSensorPrivacyReminders(int userId, int sensor,
                 IBinder token, boolean suppress) {
             enforceManageSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             Objects.requireNonNull(token);
 
             Pair<Integer, UserHandle> key = new Pair<>(sensor, UserHandle.of(userId));
@@ -1265,10 +1267,14 @@
                 case MICROPHONE:
                     mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled,
                             mAppOpsRestrictionToken);
+                    mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled,
+                            mAppOpsRestrictionToken);
                     break;
                 case CAMERA:
                     mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled,
                             mAppOpsRestrictionToken);
+                    mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_CAMERA, enabled,
+                            mAppOpsRestrictionToken);
                     break;
             }
         }
@@ -1898,9 +1904,9 @@
                 if (!mIsInEmergencyCall) {
                     mIsInEmergencyCall = true;
                     if (mSensorPrivacyServiceImpl
-                            .isIndividualSensorPrivacyEnabled(getCurrentUser(), MICROPHONE)) {
+                            .isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE)) {
                         mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
-                                getCurrentUser(), OTHER, MICROPHONE, false);
+                                mCurrentUser, OTHER, MICROPHONE, false);
                         mMicUnmutedForEmergencyCall = true;
                     } else {
                         mMicUnmutedForEmergencyCall = false;
@@ -1915,7 +1921,7 @@
                     mIsInEmergencyCall = false;
                     if (mMicUnmutedForEmergencyCall) {
                         mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
-                                getCurrentUser(), OTHER, MICROPHONE, true);
+                                mCurrentUser, OTHER, MICROPHONE, true);
                         mMicUnmutedForEmergencyCall = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 07847f1..45e6f1ec 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -102,6 +102,7 @@
 import android.appwidget.AppWidgetManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
 import android.content.Context;
@@ -307,6 +308,7 @@
      */
     @ChangeId
     @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
+    @Overridable
     static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;
 
     /**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9f41c8b..ae14ca7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -841,6 +841,12 @@
 
     public void noteEvent(final int code, final String name, final int uid) {
         enforceCallingPermission();
+        if (name == null) {
+            // TODO(b/194733136): Replace with an IllegalArgumentException throw.
+            Slog.wtfStack(TAG, "noteEvent called with null name. code = " + code);
+            return;
+        }
+
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9dbb707..7c336d7 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcLocksReader;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ServiceThread;
 
@@ -319,6 +320,7 @@
     private int mPersistentCompactionCount;
     private int mBfgsCompactionCount;
     private final ProcessDependencies mProcessDependencies;
+    private final ProcLocksReader mProcLocksReader;
 
     public CachedAppOptimizer(ActivityManagerService am) {
         this(am, null, new DefaultProcessDependencies());
@@ -335,6 +337,7 @@
         mProcessDependencies = processDependencies;
         mTestCallback = callback;
         mSettingsObserver = new SettingsContentObserver();
+        mProcLocksReader = new ProcLocksReader();
     }
 
     /**
@@ -1312,7 +1315,7 @@
 
             try {
                 // pre-check for locks to avoid unnecessary freeze/unfreeze operations
-                if (Process.hasFileLocks(pid)) {
+                if (mProcLocksReader.hasFileLocks(pid)) {
                     if (DEBUG_FREEZER) {
                         Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
                     }
@@ -1399,7 +1402,7 @@
 
             try {
                 // post-check to prevent races
-                if (Process.hasFileLocks(pid)) {
+                if (mProcLocksReader.hasFileLocks(pid)) {
                     if (DEBUG_FREEZER) {
                         Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
                     }
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index ca31681..b07684c 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -365,6 +365,9 @@
     private int onPhantomProcessFdEvent(FileDescriptor fd, int events) {
         synchronized (mLock) {
             final PhantomProcessRecord proc = mPhantomProcessesPidFds.get(fd.getInt$());
+            if (proc == null) {
+                return 0;
+            }
             if ((events & EVENT_INPUT) != 0) {
                 proc.onProcDied(true);
             } else {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a9905dc..64b9bd9 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -45,6 +45,9 @@
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED;
 import static android.app.AppOpsManager.OpEventProxyInfo;
 import static android.app.AppOpsManager.RestrictionBypass;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
@@ -1238,6 +1241,11 @@
                     scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
                             tag, true, event.getAttributionFlags(), event.getAttributionChainId());
                 }
+                // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND
+                // TODO ntmyren: figure out how to get the real mode.
+                scheduleOpStartedIfNeededLocked(parent.op, parent.uid, parent.packageName,
+                        tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED,
+                        event.getAttributionFlags(), event.getAttributionChainId());
             }
             mPausedInProgressEvents = null;
         }
@@ -3438,7 +3446,7 @@
                         + " package " + packageName + "flags: " +
                         AppOpsManager.flagsToString(flags));
                 return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
-                        packageName + " flags: " + AppOpsManager.flagsToString(flags));
+                        packageName);
             }
             final Op op = getOpLocked(ops, code, uid, true);
             final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3945,13 +3953,15 @@
         }
 
         boolean isRestricted = false;
+        int startType = START_TYPE_FAILED;
         synchronized (this) {
             final Ops ops = getOpsLocked(uid, packageName, attributionTag,
                     pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
             if (ops == null) {
                 if (!dryRun) {
                     scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
-                            flags, AppOpsManager.MODE_IGNORED);
+                            flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags,
+                            attributionChainId);
                 }
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName + " flags: "
@@ -3977,7 +3987,7 @@
                     if (!dryRun) {
                         attributedOp.rejected(uidState.state, flags);
                         scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
-                                flags, uidMode);
+                                flags, uidMode, startType, attributionFlags, attributionChainId);
                     }
                     return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
                 }
@@ -3993,7 +4003,7 @@
                     if (!dryRun) {
                         attributedOp.rejected(uidState.state, flags);
                         scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
-                                flags, mode);
+                                flags, mode, startType, attributionFlags, attributionChainId);
                     }
                     return new SyncNotedAppOp(mode, code, attributionTag, packageName);
                 }
@@ -4011,12 +4021,14 @@
                         attributedOp.started(clientId, proxyUid, proxyPackageName,
                                 proxyAttributionTag, uidState.state, flags, attributionFlags,
                                 attributionChainId);
+                        startType = START_TYPE_STARTED;
                     }
                 } catch (RemoteException e) {
                     throw new RuntimeException(e);
                 }
                 scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
-                        isRestricted ? MODE_IGNORED : MODE_ALLOWED);
+                        isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags,
+                        attributionChainId);
             }
         }
 
@@ -4187,7 +4199,9 @@
     }
 
     private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
-            String attributionTag, @OpFlags int flags, @Mode int result) {
+            String attributionTag, @OpFlags int flags, @Mode int result,
+            @AppOpsManager.OnOpStartedListener.StartedType int startedType,
+            @AttributionFlags int attributionFlags, int attributionChainId) {
         ArraySet<StartedCallback> dispatchedCallbacks = null;
         final int callbackListCount = mStartedWatchers.size();
         for (int i = 0; i < callbackListCount; i++) {
@@ -4213,12 +4227,13 @@
         mHandler.sendMessage(PooledLambda.obtainMessage(
                 AppOpsService::notifyOpStarted,
                 this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags,
-                result));
+                result, startedType, attributionFlags, attributionChainId));
     }
 
     private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
             int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
-            @Mode int result) {
+            @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType,
+            @AttributionFlags int attributionFlags, int attributionChainId) {
         final long identity = Binder.clearCallingIdentity();
         try {
             final int callbackCount = callbacks.size();
@@ -4226,7 +4241,7 @@
                 final StartedCallback callback = callbacks.valueAt(i);
                 try {
                     callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags,
-                            result);
+                            result, startedType, attributionFlags, attributionChainId);
                 } catch (RemoteException e) {
                     /* do nothing */
                 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f7d0914..34e2578 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -736,6 +736,11 @@
     private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT;
     private long mLoweredFromNormalToVibrateTime;
 
+    // Uid of the active hotword detection service to check if caller is the one or not.
+    @GuardedBy("mHotwordDetectionServiceUidLock")
+    private int mHotwordDetectionServiceUid = android.os.Process.INVALID_UID;
+    private final Object mHotwordDetectionServiceUidLock = new Object();
+
     // Array of Uids of valid accessibility services to check if caller is one of them
     private final Object mAccessibilityServiceUidsLock = new Object();
     @GuardedBy("mAccessibilityServiceUidsLock")
@@ -1337,6 +1342,9 @@
             updateAssistantUId(true);
             AudioSystem.setRttEnabled(mRttEnabled);
         }
+        synchronized (mHotwordDetectionServiceUidLock) {
+            AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid);
+        }
         synchronized (mAccessibilityServiceUidsLock) {
             AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
         }
@@ -9108,6 +9116,16 @@
         }
 
         @Override
+        public void setHotwordDetectionServiceUid(int uid) {
+            synchronized (mHotwordDetectionServiceUidLock) {
+                if (mHotwordDetectionServiceUid != uid) {
+                    mHotwordDetectionServiceUid = uid;
+                    AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid);
+                }
+            }
+        }
+
+        @Override
         public void setAccessibilityServiceUids(IntArray uids) {
             synchronized (mAccessibilityServiceUidsLock) {
                 if (uids.size() == 0) {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 973fbd2..6d56780 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -367,6 +367,14 @@
     }
 
     /**
+     * Same as {@link AudioSystem#setHotwordDetectionServiceUid(int)}
+     * Communicate UID of current HotwordDetectionService to audio policy service.
+     */
+    public int setHotwordDetectionServiceUid(int uid) {
+        return AudioSystem.setHotwordDetectionServiceUid(uid);
+    }
+
+    /**
      * Same as {@link AudioSystem#setCurrentImeUid(int)}
      * Communicate UID of current InputMethodService to audio policy service.
      */
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4973d45..b1d300c 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1106,6 +1106,11 @@
             return Settings.Secure.getInt(context.getContentResolver(),
                     CoexCoordinator.SETTING_ENABLE_NAME, 1) != 0;
         }
+
+        public boolean isCoexFaceNonBypassHapticsDisabled(Context context) {
+            return Settings.Secure.getInt(context.getContentResolver(),
+                    CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0;
+        }
     }
 
     /**
@@ -1137,6 +1142,8 @@
         //  by default.
         CoexCoordinator coexCoordinator = CoexCoordinator.getInstance();
         coexCoordinator.setAdvancedLogicEnabled(injector.isAdvancedCoexLogicEnabled(context));
+        coexCoordinator.setFaceHapticDisabledWhenNonBypass(
+                injector.isCoexFaceNonBypassHapticsDisabled(context));
 
         try {
             injector.getActivityManagerService().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 013c74d..6f38ed0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -54,12 +54,18 @@
     public static final int STATE_NEW = 0;
     // Framework/HAL have started this operation
     public static final int STATE_STARTED = 1;
-    // Operation is started, but requires some user action (such as finger lift & re-touch)
+    // Operation is started, but requires some user action to start (such as finger lift & re-touch)
     public static final int STATE_STARTED_PAUSED = 2;
+    // Same as above, except auth was attempted (rejected, timed out, etc).
+    public static final int STATE_STARTED_PAUSED_ATTEMPTED = 3;
     // Done, errored, canceled, etc. HAL/framework are not running this sensor anymore.
-    public static final int STATE_STOPPED = 3;
+    public static final int STATE_STOPPED = 4;
 
-    @IntDef({STATE_NEW, STATE_STARTED, STATE_STARTED_PAUSED, STATE_STOPPED})
+    @IntDef({STATE_NEW,
+            STATE_STARTED,
+            STATE_STARTED_PAUSED,
+            STATE_STARTED_PAUSED_ATTEMPTED,
+            STATE_STOPPED})
     @interface State {}
 
     private final boolean mIsStrongBiometric;
@@ -76,7 +82,7 @@
 
     private long mStartTimeMs;
 
-    protected boolean mAuthAttempted;
+    private boolean mAuthAttempted;
 
     // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
     //  the state. We should think of a way to improve this in the future.
@@ -92,6 +98,12 @@
      */
     protected abstract void handleLifecycleAfterAuth(boolean authenticated);
 
+    /**
+     * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched.
+     *         etc)
+     */
+    public abstract boolean wasUserDetected();
+
     public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
             int targetUserId, long operationId, boolean restricted, @NonNull String owner,
@@ -174,7 +186,8 @@
                 + ", isBP: " + isBiometricPrompt()
                 + ", listener: " + listener
                 + ", requireConfirmation: " + mRequireConfirmation
-                + ", user: " + getTargetUserId());
+                + ", user: " + getTargetUserId()
+                + ", clientMonitor: " + toString());
 
         final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
         if (isCryptoOperation()) {
@@ -298,6 +311,11 @@
                 public void handleLifecycleAfterAuth() {
                     AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
                 }
+
+                @Override
+                public void sendAuthenticationCanceled() {
+                    sendCancelOnly(listener);
+                }
             });
         } else {
             // Allow system-defined limit of number of attempts before giving up
@@ -332,10 +350,30 @@
                 public void handleLifecycleAfterAuth() {
                     AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
                 }
+
+                @Override
+                public void sendAuthenticationCanceled() {
+                    sendCancelOnly(listener);
+                }
             });
         }
     }
 
+    private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) {
+        if (listener == null) {
+            Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null");
+            return;
+        }
+        try {
+            listener.onError(getSensorId(),
+                    getCookie(),
+                    BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                    0 /* vendorCode */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
     @Override
     public void onAcquired(int acquiredInfo, int vendorCode) {
         super.onAcquired(acquiredInfo, vendorCode);
@@ -349,9 +387,11 @@
     }
 
     @Override
-    public void onError(int errorCode, int vendorCode) {
+    public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) {
         super.onError(errorCode, vendorCode);
         mState = STATE_STOPPED;
+
+        CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError);
     }
 
     /**
@@ -413,4 +453,8 @@
     public boolean interruptsPrecedingClients() {
         return true;
     }
+
+    public boolean wasAuthAttempted() {
+        return mAuthAttempted;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
index b576673..25d4a38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Slog;
@@ -45,6 +46,8 @@
     private static final String TAG = "BiometricCoexCoordinator";
     public static final String SETTING_ENABLE_NAME =
             "com.android.server.biometrics.sensors.CoexCoordinator.enable";
+    public static final String FACE_HAPTIC_DISABLE =
+            "com.android.server.biometrics.sensors.CoexCoordinator.disable_face_haptics";
     private static final boolean DEBUG = true;
 
     // Successful authentications should be used within this amount of time.
@@ -52,7 +55,7 @@
 
     /**
      * Callback interface notifying the owner of "results" from the CoexCoordinator's business
-     * logic.
+     * logic for accept and reject.
      */
     interface Callback {
         /**
@@ -71,6 +74,22 @@
          * from scheduler if auth was successful).
          */
         void handleLifecycleAfterAuth();
+
+        /**
+         * Requests the owner to notify the caller that authentication was canceled.
+         */
+        void sendAuthenticationCanceled();
+    }
+
+    /**
+     * Callback interface notifying the owner of "results" from the CoexCoordinator's business
+     * logic for errors.
+     */
+    interface ErrorCallback {
+        /**
+         * Requests the owner to initiate a vibration for this event.
+         */
+        void sendHapticFeedback();
     }
 
     private static CoexCoordinator sInstance;
@@ -144,6 +163,10 @@
         mAdvancedLogicEnabled = enabled;
     }
 
+    public void setFaceHapticDisabledWhenNonBypass(boolean disabled) {
+        mFaceHapticDisabledWhenNonBypass = disabled;
+    }
+
     @VisibleForTesting
     void reset() {
         mClientMap.clear();
@@ -153,6 +176,7 @@
     private final Map<Integer, AuthenticationClient<?>> mClientMap;
     @VisibleForTesting final LinkedList<SuccessfulAuth> mSuccessfulAuths;
     private boolean mAdvancedLogicEnabled;
+    private boolean mFaceHapticDisabledWhenNonBypass;
     private final Handler mHandler;
 
     private CoexCoordinator() {
@@ -191,6 +215,9 @@
         mClientMap.remove(sensorType);
     }
 
+    /**
+     * Notify the coordinator that authentication succeeded (accepted)
+     */
     public void onAuthenticationSucceeded(long currentTimeMillis,
             @NonNull AuthenticationClient<?> client,
             @NonNull Callback callback) {
@@ -225,7 +252,11 @@
                         mSuccessfulAuths.add(new SuccessfulAuth(mHandler, mSuccessfulAuths,
                                 currentTimeMillis, SENSOR_TYPE_FACE, client, callback));
                     } else {
-                        callback.sendHapticFeedback();
+                        if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
+                            Slog.w(TAG, "Skipping face success haptic");
+                        } else {
+                            callback.sendHapticFeedback();
+                        }
                         callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
                         callback.handleLifecycleAfterAuth();
                     }
@@ -257,6 +288,9 @@
         }
     }
 
+    /**
+     * Notify the coordinator that a rejection has occurred.
+     */
     public void onAuthenticationRejected(long currentTimeMillis,
             @NonNull AuthenticationClient<?> client,
             @LockoutTracker.LockoutMode int lockoutMode,
@@ -278,12 +312,23 @@
                         // BiometricScheduler do not get stuck.
                         Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps);
                         callback.handleLifecycleAfterAuth();
-                    } else {
-                        // UDFPS is not actively authenticating (finger not touching, already
-                        // rejected, etc).
+                    } else if (isUdfpsAuthAttempted(udfps)) {
+                        // If UDFPS is STATE_STARTED_PAUSED (e.g. finger rejected but can still
+                        // auth after pointer goes down, it means UDFPS encountered a rejection. In
+                        // this case, we need to play the final reject haptic since face auth is
+                        // also done now.
                         callback.sendHapticFeedback();
                         callback.handleLifecycleAfterAuth();
                     }
+                    else {
+                        // UDFPS auth has never been attempted.
+                        if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
+                            Slog.w(TAG, "Skipping face reject haptic");
+                        } else {
+                            callback.sendHapticFeedback();
+                        }
+                        callback.handleLifecycleAfterAuth();
+                    }
                 } else if (isCurrentUdfps(client)) {
                     // Face should either be running, or have already finished
                     SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis);
@@ -330,11 +375,63 @@
         }
     }
 
+    /**
+     * Notify the coordinator that an error has occurred.
+     */
+    public void onAuthenticationError(@NonNull AuthenticationClient<?> client,
+            @BiometricConstants.Errors int error, @NonNull ErrorCallback callback) {
+        // Figure out non-coex state
+        final boolean shouldUsuallyVibrate;
+        if (isCurrentFaceAuth(client)) {
+            final boolean notDetectedOnKeyguard = client.isKeyguard() && !client.wasUserDetected();
+            final boolean authAttempted = client.wasAuthAttempted();
+
+            switch (error) {
+                case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+                    shouldUsuallyVibrate = authAttempted && !notDetectedOnKeyguard;
+                    break;
+                default:
+                    shouldUsuallyVibrate = false;
+                    break;
+            }
+        } else {
+            shouldUsuallyVibrate = false;
+        }
+
+        // Figure out coex state
+        final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard();
+        final boolean hapticSuppressedByCoex;
+
+        if (keyguardAdvancedLogic) {
+            if (isSingleAuthOnly(client)) {
+                hapticSuppressedByCoex = false;
+            } else {
+                hapticSuppressedByCoex = isCurrentFaceAuth(client)
+                        && !client.isKeyguardBypassEnabled();
+            }
+        } else {
+            hapticSuppressedByCoex = false;
+        }
+
+        // Combine and send feedback if appropriate
+        Slog.d(TAG, "shouldUsuallyVibrate: " + shouldUsuallyVibrate
+                + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex);
+        if (shouldUsuallyVibrate && !hapticSuppressedByCoex) {
+            callback.sendHapticFeedback();
+        }
+    }
+
     @Nullable
     private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) {
         for (SuccessfulAuth auth : mSuccessfulAuths) {
             if (currentTimeMillis - auth.mAuthTimestamp >= SUCCESSFUL_AUTH_VALID_DURATION_MS) {
-                Slog.d(TAG, "Removing stale auth: " + auth);
+                // TODO(b/193089985): This removes the auth but does not notify the client with
+                //  an appropriate lifecycle event (such as ERROR_CANCELED), and violates the
+                //  API contract. However, this might be OK for now since the validity duration
+                //  is way longer than the time it takes to auth with fingerprint.
+                Slog.e(TAG, "Removing stale auth: " + auth);
                 mSuccessfulAuths.remove(auth);
             } else if (auth.mSensorType == SENSOR_TYPE_FACE) {
                 mSuccessfulAuths.remove(auth);
@@ -345,9 +442,13 @@
     }
 
     private void removeAndFinishAllFaceFromQueue() {
+        // Note that these auth are all successful, but have never notified the client (e.g.
+        // keyguard). To comply with the authentication lifecycle, we must notify the client that
+        // auth is "done". The safest thing to do is to send ERROR_CANCELED.
         for (SuccessfulAuth auth : mSuccessfulAuths) {
             if (auth.mSensorType == SENSOR_TYPE_FACE) {
-                Slog.d(TAG, "Removing from queue and finishing: " + auth);
+                Slog.d(TAG, "Removing from queue, canceling, and finishing: " + auth);
+                auth.mCallback.sendAuthenticationCanceled();
                 auth.mCallback.handleLifecycleAfterAuth();
                 mSuccessfulAuths.remove(auth);
             }
@@ -374,6 +475,13 @@
         return false;
     }
 
+    private static boolean isUdfpsAuthAttempted(@Nullable AuthenticationClient<?> client) {
+        if (client instanceof Udfps) {
+            return client.getState() == AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED;
+        }
+        return false;
+    }
+
     private boolean isUnknownClient(@NonNull AuthenticationClient<?> client) {
         for (AuthenticationClient<?> c : mClientMap.values()) {
             if (c == client) {
@@ -400,6 +508,7 @@
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("Enabled: ").append(mAdvancedLogicEnabled);
+        sb.append(", Face Haptic Disabled: ").append(mFaceHapticDisabledWhenNonBypass);
         sb.append(", Queue size: " ).append(mSuccessfulAuths.size());
         for (SuccessfulAuth auth : mSuccessfulAuths) {
             sb.append(", Auth: ").append(auth.toString());
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index f7fd8d0..d66a279 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -127,7 +127,8 @@
         }
     }
 
-    private boolean wasUserDetected() {
+    @Override
+    public boolean wasUserDetected() {
         // Do not provide haptic feedback if the user was not detected, and an error (usually
         // ERROR_TIMEOUT) is received.
         return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
@@ -160,7 +161,7 @@
     }
 
     @Override
-    public void onError(int error, int vendorCode) {
+    public void onError(@BiometricConstants.Errors int error, int vendorCode) {
         mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
                 getStartTimeMs(),
                 System.currentTimeMillis() - getStartTimeMs() /* latency */,
@@ -169,25 +170,8 @@
                 vendorCode,
                 getTargetUserId()));
 
-        switch (error) {
-            case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
-                if (!wasUserDetected() && !isBiometricPrompt()) {
-                    // No vibration if user was not detected on keyguard
-                    break;
-                }
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
-                if (mAuthAttempted) {
-                    // Only vibrate if auth was attempted. If the user was already locked out prior
-                    // to starting authentication, do not vibrate.
-                    vibrateError();
-                }
-                break;
-            case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL:
-                BiometricNotificationUtils.showReEnrollmentNotification(getContext());
-                break;
-            default:
-                break;
+        if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) {
+            BiometricNotificationUtils.showReEnrollmentNotification(getContext());
         }
 
         super.onError(error, vendorCode);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index c33b957..33950af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -115,7 +115,8 @@
         }
     }
 
-    private boolean wasUserDetected() {
+    @Override
+    public boolean wasUserDetected() {
         // Do not provide haptic feedback if the user was not detected, and an error (usually
         // ERROR_TIMEOUT) is received.
         return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
@@ -147,7 +148,7 @@
     }
 
     @Override
-    public void onError(int error, int vendorCode) {
+    public void onError(@BiometricConstants.Errors int error, int vendorCode) {
         mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
                 getStartTimeMs(),
                 System.currentTimeMillis() - getStartTimeMs() /* latency */,
@@ -156,24 +157,6 @@
                 vendorCode,
                 getTargetUserId()));
 
-        switch (error) {
-            case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
-                if (!wasUserDetected() && !isBiometricPrompt()) {
-                    // No vibration if user was not detected on keyguard
-                    break;
-                }
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
-                if (mAuthAttempted) {
-                    // Only vibrate if auth was attempted. If the user was already locked out prior
-                    // to starting authentication, do not vibrate.
-                    vibrateError();
-                }
-                break;
-            default:
-                break;
-        }
-
         super.onError(error, vendorCode);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 99e6e62..37ee76a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -106,6 +106,12 @@
     }
 
     @Override
+    public boolean wasUserDetected() {
+        // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout
+        return false;
+    }
+
+    @Override
     public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> token) {
         super.onAuthenticated(identifier, authenticated, token);
@@ -114,7 +120,7 @@
             mState = STATE_STOPPED;
             UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
         } else {
-            mState = STATE_STARTED_PAUSED;
+            mState = STATE_STARTED_PAUSED_ATTEMPTED;
         }
     }
 
@@ -188,7 +194,7 @@
     public void onPointerUp() {
         try {
             mIsPointerDown = false;
-            mState = STATE_STARTED_PAUSED;
+            mState = STATE_STARTED_PAUSED_ATTEMPTED;
             mALSProbeCallback.getProbe().disable();
             getFreshDaemon().onPointerUp(0 /* pointerId */);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 7558d15..5060744 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -112,7 +112,7 @@
             resetFailedAttempts(getTargetUserId());
             UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
         } else {
-            mState = STATE_STARTED_PAUSED;
+            mState = STATE_STARTED_PAUSED_ATTEMPTED;
             final @LockoutTracker.LockoutMode int lockoutMode =
                     mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
             if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
@@ -153,6 +153,12 @@
     }
 
     @Override
+    public boolean wasUserDetected() {
+        // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout
+        return false;
+    }
+
+    @Override
     public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
         mLockoutFrameworkImpl.addFailedAttemptForUser(userId);
         return super.handleFailedAttempt(userId);
@@ -206,7 +212,7 @@
     @Override
     public void onPointerUp() {
         mIsPointerDown = false;
-        mState = STATE_STARTED_PAUSED;
+        mState = STATE_STARTED_PAUSED_ATTEMPTED;
         mALSProbeCallback.getProbe().disable();
         UdfpsHelper.onFingerUp(getFreshDaemon());
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7d06d6e..afd1889 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -3285,6 +3285,9 @@
 
             synchronized (mSyncRoot) {
                 final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+                if (display == null) {
+                    return null;
+                }
                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
                 if (device == null) {
                     return null;
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index f23ae6e2..186986e 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -153,7 +153,7 @@
                 updateVoteLocked(displayId, priority, vote);
             }
         };
-        mSensorObserver = new SensorObserver(context, ballotBox);
+        mSensorObserver = new SensorObserver(context, ballotBox, injector);
         mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
         mDeviceConfig = injector.getDeviceConfig();
@@ -2127,27 +2127,34 @@
         }
     }
 
-    private static class SensorObserver implements ProximityActiveListener {
-        private static final String PROXIMITY_SENSOR_NAME = null;
-        private static final String PROXIMITY_SENSOR_TYPE = Sensor.STRING_TYPE_PROXIMITY;
+    private final class SensorObserver implements ProximityActiveListener,
+            DisplayManager.DisplayListener {
+        private final String mProximitySensorName = null;
+        private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
 
         private final BallotBox mBallotBox;
         private final Context mContext;
+        private final Injector mInjector;
 
         private DisplayManager mDisplayManager;
         private DisplayManagerInternal mDisplayManagerInternal;
         private boolean mIsProxActive = false;
+        private final SparseBooleanArray mDozeStateByDisplay;
 
-        SensorObserver(Context context, BallotBox ballotBox) {
+        SensorObserver(Context context, BallotBox ballotBox, Injector injector) {
             mContext = context;
             mBallotBox = ballotBox;
+            mInjector = injector;
+            mDozeStateByDisplay = new SparseBooleanArray();
         }
 
         @Override
         public void onProximityActive(boolean isActive) {
-            if (mIsProxActive != isActive) {
-                mIsProxActive = isActive;
-                recalculateVotes();
+            synchronized (mLock) {
+                if (mIsProxActive != isActive) {
+                    mIsProxActive = isActive;
+                    recalculateVotesLocked();
+                }
             }
         }
 
@@ -2158,17 +2165,27 @@
             final SensorManagerInternal sensorManager =
                     LocalServices.getService(SensorManagerInternal.class);
             sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
+
+            synchronized (mLock) {
+                for (Display d : mDisplayManager.getDisplays()) {
+                    mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
+                }
+            }
+            mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
+                    DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                            | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                            | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
         }
 
-        private void recalculateVotes() {
+        private void recalculateVotesLocked() {
             final Display[] displays = mDisplayManager.getDisplays();
             for (Display d : displays) {
                 int displayId = d.getDisplayId();
                 Vote vote = null;
-                if (mIsProxActive) {
+                if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
                     final RefreshRateRange rate =
                             mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
-                                    displayId, PROXIMITY_SENSOR_NAME, PROXIMITY_SENSOR_TYPE);
+                                    displayId, mProximitySensorName, mProximitySensorType);
                     if (rate != null) {
                         vote = Vote.forRefreshRates(rate.min, rate.max);
                     }
@@ -2180,6 +2197,41 @@
         void dumpLocked(PrintWriter pw) {
             pw.println("  SensorObserver");
             pw.println("    mIsProxActive=" + mIsProxActive);
+            pw.println("    mDozeStateByDisplay:");
+            for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
+                final int id = mDozeStateByDisplay.keyAt(i);
+                final boolean dozed = mDozeStateByDisplay.valueAt(i);
+                pw.println("      " + id + " -> " + dozed);
+            }
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId));
+            synchronized (mLock) {
+                mDozeStateByDisplay.put(displayId, isDozeState);
+                recalculateVotesLocked();
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            boolean wasDozeState = mDozeStateByDisplay.get(displayId);
+            synchronized (mLock) {
+                mDozeStateByDisplay.put(displayId,
+                        mInjector.isDozeState(mDisplayManager.getDisplay(displayId)));
+                if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
+                    recalculateVotesLocked();
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            synchronized (mLock) {
+                mDozeStateByDisplay.delete(displayId);
+                recalculateVotesLocked();
+            }
         }
     }
 
@@ -2411,6 +2463,8 @@
                 Handler handler, long flags);
 
         BrightnessInfo getBrightnessInfo(int displayId);
+
+        boolean isDozeState(Display d);
     }
 
     @VisibleForTesting
@@ -2463,6 +2517,11 @@
             return null;
         }
 
+        @Override
+        public boolean isDozeState(Display d) {
+            return Display.isDozeState(d.getState());
+        }
+
         private DisplayManager getDisplayManager() {
             if (mDisplayManager == null) {
                 mDisplayManager = mContext.getSystemService(DisplayManager.class);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 59f536c..1108937 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2342,7 +2342,7 @@
             try {
                 // TODO(brightnessfloat): change BatteryStats to use float
                 mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
-                        brightness, null));
+                        brightness));
             } catch (RemoteException e) {
                 // same process
             }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 24d29d3..f953cc8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -795,12 +795,11 @@
                             mBacklightAdapter.setBacklight(sdrBacklight, sdrNits, backlight, nits);
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "ScreenBrightness",
-                                    BrightnessSynchronizer.brightnessFloatToInt(
-                                            brightnessState, null));
+                                    BrightnessSynchronizer.brightnessFloatToInt(brightnessState));
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "SdrScreenBrightness",
                                     BrightnessSynchronizer.brightnessFloatToInt(
-                                            sdrBrightnessState, null));
+                                            sdrBrightnessState));
                         } finally {
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9fcc9a1..6fb9e58a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3230,7 +3230,7 @@
      * Interface for the system to handle request from InputMonitors.
      */
     private final class InputMonitorHost extends IInputMonitorHost.Stub {
-        private final IBinder mToken;
+        private IBinder mToken;
 
         InputMonitorHost(IBinder token) {
             mToken = token;
@@ -3238,12 +3238,23 @@
 
         @Override
         public void pilferPointers() {
+            if (mToken == null) {
+                throw new IllegalStateException(
+                        "Illegal call to pilferPointers after InputMonitorHost is disposed.");
+            }
             nativePilferPointers(mPtr, mToken);
         }
 
         @Override
         public void dispose() {
-            nativeRemoveInputChannel(mPtr, mToken);
+            // We do not remove the input monitor here by calling nativeRemoveInputChannel because
+            // it causes a race in InputDispatcher between the removal of the InputChannel through
+            // that call and the InputChannel#dispose call (which causes an FD hangup) from the
+            // client (b/189135695).
+            //
+            // NOTE: This means the client is responsible for properly closing the InputMonitor by
+            // disposing the InputChannel and all its duplicates.
+            mToken = null;
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 9d80b9c..2328dfc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -116,8 +116,10 @@
      *
      * @param windowToken the window token that is now in control, or {@code null} if no client
      *                   window is in control of the IME.
+     * @param imeParentChanged {@code true} when the window manager thoughts the IME surface parent
+     *                         will end up to change later, or {@code false} otherwise.
      */
-    public abstract void reportImeControl(@Nullable IBinder windowToken);
+    public abstract void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged);
 
     /**
      * Destroys the IME surface.
@@ -176,7 +178,8 @@
                 }
 
                 @Override
-                public void reportImeControl(@Nullable IBinder windowToken) {
+                public void reportImeControl(@Nullable IBinder windowToken,
+                        boolean imeParentChanged) {
                 }
 
                 @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fd0f1c3..dc95533 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4937,13 +4937,19 @@
         return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
     }
 
-    private void reportImeControl(@Nullable IBinder windowToken) {
+    private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
         synchronized (mMethodMap) {
             if (mCurFocusedWindow != windowToken) {
                 // mCurPerceptible was set by the focused window, but it is no longer in control,
                 // so we reset mCurPerceptible.
                 mCurPerceptible = true;
             }
+            if (imeParentChanged) {
+                // Hide the IME method menu earlier when the IME surface parent will change in
+                // case seeing the dialog dismiss flickering during the next focused window
+                // starting the input connection.
+                mMenuController.hideInputMethodMenu();
+            }
         }
     }
 
@@ -5001,8 +5007,8 @@
         }
 
         @Override
-        public void reportImeControl(@Nullable IBinder windowToken) {
-            mService.reportImeControl(windowToken);
+        public void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
+            mService.reportImeControl(windowToken, imeParentChanged);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index ce195e6..aa4fa7c 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -225,7 +225,8 @@
                         }
 
                         @Override
-                        public void reportImeControl(@Nullable IBinder windowToken) {
+                        public void reportImeControl(@Nullable IBinder windowToken,
+                                boolean imeParentChanged) {
                         }
 
                         @Override
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index bebe6ed..91f14de 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -293,7 +293,7 @@
                             + ": brightness=" + brightness);
                     return;
                 }
-                int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(brightness, null);
+                int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(brightness);
                 int color = brightnessInt & 0x000000ff;
                 color = 0xff000000 | (color << 16) | (color << 8) | color;
                 setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4ee867b..097b071 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -289,8 +289,7 @@
     private String mActiveIface;
 
     /** Set of any ifaces associated with mobile networks since boot. */
-    @GuardedBy("mStatsLock")
-    private String[] mMobileIfaces = new String[0];
+    private volatile String[] mMobileIfaces = new String[0];
 
     /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
     @GuardedBy("mStatsLock")
@@ -935,7 +934,12 @@
 
     @Override
     public String[] getMobileIfaces() {
-        return mMobileIfaces;
+        // TODO (b/192758557): Remove debug log.
+        if (ArrayUtils.contains(mMobileIfaces, null)) {
+            throw new NullPointerException(
+                    "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
+        }
+        return mMobileIfaces.clone();
     }
 
     @Override
@@ -1084,7 +1088,8 @@
     }
 
     @Override
-    public long getIfaceStats(String iface, int type) {
+    public long getIfaceStats(@NonNull String iface, int type) {
+        Objects.requireNonNull(iface);
         long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
         if (nativeIfaceStats == -1) {
             return nativeIfaceStats;
@@ -1382,7 +1387,12 @@
             }
         }
 
-        mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
+        mMobileIfaces = mobileIfaces.toArray(new String[0]);
+        // TODO (b/192758557): Remove debug log.
+        if (ArrayUtils.contains(mMobileIfaces, null)) {
+            throw new NullPointerException(
+                    "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
+        }
     }
 
     private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 06ff691..ed9b539 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -1287,20 +1287,6 @@
                 }
             }
 
-            // This package isn't technically installed and won't be written to settings, so we can
-            // treat it as filtered until it's available again.
-            final AndroidPackage targetPkg = targetPkgSetting.pkg;
-            if (targetPkg == null) {
-                if (DEBUG_LOGGING) {
-                    Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
-                }
-                return true;
-            }
-            if (targetPkg.isStaticSharedLibrary()) {
-                // not an app, this filtering takes place at a higher level
-                return false;
-            }
-            final String targetName = targetPkg.getPackageName();
             if (DEBUG_TRACING) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
             }
@@ -1343,6 +1329,21 @@
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
             }
+
+            // This package isn't technically installed and won't be written to settings, so we can
+            // treat it as filtered until it's available again.
+            final AndroidPackage targetPkg = targetPkgSetting.pkg;
+            if (targetPkg == null) {
+                if (DEBUG_LOGGING) {
+                    Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
+                }
+                return true;
+            }
+            if (targetPkg.isStaticSharedLibrary()) {
+                // not an app, this filtering takes place at a higher level
+                return false;
+            }
+
             try {
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
@@ -1415,6 +1416,7 @@
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
                 }
+                final String targetName = targetPkg.getPackageName();
                 if (callingSharedPkgSettings != null) {
                     int size = callingSharedPkgSettings.size();
                     for (int index = 0; index < size; index++) {
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index 7627281..3101ca7 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -118,12 +118,19 @@
      * @param progress Value between [0, 1].
      */
     public void setProgress(float progress) {
+        final boolean oldLoadingState;
         final boolean newLoadingState;
         synchronized (mLock) {
-            updateProgressLocked(progress);
+            oldLoadingState = mLoadingState.isLoading();
+            if (oldLoadingState) {
+                // Due to asynchronous progress reporting, incomplete progress might be received
+                // after the app is migrated off incremental. Ignore such progress updates.
+                updateProgressLocked(progress);
+            }
             newLoadingState = mLoadingState.isLoading();
         }
-        if (!newLoadingState) {
+        if (oldLoadingState && !newLoadingState) {
+            // Only report the state change when loading state changes from true to false
             onLoadingStateChanged();
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee44c10..904a1f0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24838,11 +24838,11 @@
             pw.println("vers,1");
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_VERSION)
-                && packageName == null) {
-            // dump version information for all volumes with installed packages
-            dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
+        // reader
+        if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+            if (!checkin) {
+                dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
+            }
         }
 
         if (!checkin
@@ -24873,8 +24873,7 @@
             ipw.decreaseIndent();
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
             final String requiredVerifierPackage = mRequiredVerifierPackage;
             if (!checkin) {
                 if (dumpState.onTitlePrinted()) {
@@ -24895,8 +24894,7 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
             final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
             final ComponentName verifierComponent = proxy.getComponentName();
             if (verifierComponent != null) {
@@ -24923,13 +24921,11 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_LIBS)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
             dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_FEATURES)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
             if (dumpState.onTitlePrinted()) {
                 pw.println();
             }
@@ -24939,7 +24935,12 @@
 
             synchronized (mAvailableFeatures) {
                 for (FeatureInfo feat : mAvailableFeatures.values()) {
-                    if (!checkin) {
+                    if (checkin) {
+                        pw.print("feat,");
+                        pw.print(feat.name);
+                        pw.print(",");
+                        pw.println(feat.version);
+                    } else {
                         pw.print("  ");
                         pw.print(feat.name);
                         if (feat.version > 0) {
@@ -24947,73 +24948,55 @@
                             pw.print(feat.version);
                         }
                         pw.println();
-                    } else {
-                        pw.print("feat,");
-                        pw.print(feat.name);
-                        pw.print(",");
-                        pw.println(feat.version);
                     }
                 }
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
             dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
             dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
             dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
             mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
             synchronized (mLock) {
                 mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
             }
@@ -25028,15 +25011,11 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_QUERIES)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
             dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
             // This cannot be moved to ComputerEngine since the set of packages in the
             // SharedUserSetting do not have a copy.
             synchronized (mLock) {
@@ -25044,9 +25023,7 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CHANGES)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
             if (dumpState.onTitlePrinted()) pw.println();
             pw.println("Package Changes:");
             synchronized (mLock) {
@@ -25073,9 +25050,7 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_FROZEN)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
             // XXX should handle packageName != null by dumping only install data that
             // the given package is involved with.
             if (dumpState.onTitlePrinted()) pw.println();
@@ -25096,9 +25071,7 @@
             ipw.decreaseIndent();
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_VOLUMES)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
             if (dumpState.onTitlePrinted()) pw.println();
 
             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
@@ -25117,61 +25090,50 @@
             ipw.decreaseIndent();
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
                 && packageName == null) {
             synchronized (mLock) {
                 mComponentResolver.dumpServicePermissions(pw, dumpState);
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DEXOPT)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
             if (dumpState.onTitlePrinted()) pw.println();
             dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
             if (dumpState.onTitlePrinted()) pw.println();
             dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
-                && packageName == null) {
-            if (!checkin) {
-                if (dumpState.onTitlePrinted()) pw.println();
-                synchronized (mLock) {
-                    mSettings.dumpReadMessagesLPr(pw, dumpState);
-                }
-                pw.println();
-                pw.println("Package warning messages:");
-                dumpCriticalInfo(pw, null);
-            } else {
-                dumpCriticalInfo(pw, "msg,");
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+            if (dumpState.onTitlePrinted()) pw.println();
+            synchronized (mLock) {
+                mSettings.dumpReadMessagesLPr(pw, dumpState);
             }
+            pw.println();
+            pw.println("Package warning messages:");
+            dumpCriticalInfo(pw, null);
+        }
+
+        if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
+            dumpCriticalInfo(pw, "msg,");
         }
 
         // PackageInstaller should be called outside of mPackages lock
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_INSTALLS)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
             // XXX should handle packageName != null by dumping only install data that
             // the given package is involved with.
             if (dumpState.onTitlePrinted()) pw.println();
             mInstallerService.dump(new IndentingPrintWriter(pw, "  ", 120));
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_APEX)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
             mApexManager.dump(pw, packageName);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
                 && packageName == null) {
             pw.println();
             pw.println("Per UID read timeouts:");
@@ -25190,9 +25152,7 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
             pw.println("Snapshot statistics");
             if (!mSnapshotEnabled) {
                 pw.println("  Snapshots disabled");
@@ -26533,7 +26493,7 @@
     }
 
     boolean readPermissionStateForUser(@UserIdInt int userId) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mPermissionManager.writeLegacyPermissionStateTEMP();
             mSettings.readPermissionStateForUserSyncLPr(userId);
             mPermissionManager.readLegacyPermissionStateTEMP();
@@ -26609,7 +26569,7 @@
         if (packageName == null || alias == null) {
             return null;
         }
-        synchronized(mLock) {
+        synchronized (mLock) {
             final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null
                     || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
@@ -26656,7 +26616,7 @@
         if (packageName == null || ks == null) {
             return false;
         }
-        synchronized(mLock) {
+        synchronized (mLock) {
             final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null
                     || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bf82bd8..88dd033 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -176,6 +176,10 @@
         name = orig.name;
         realName = orig.realName;
         doCopy(orig);
+        // Clone the user states.
+        for (int i = 0; i < mUserState.size(); i++) {
+            mUserState.put(mUserState.keyAt(i), new PackageUserState(mUserState.valueAt(i)));
+        }
     }
 
     public void setInstallerPackageName(String packageName) {
@@ -314,6 +318,7 @@
 
     void setInstalled(boolean inst, int userId) {
         modifyUserState(userId).installed = inst;
+        onChanged();
     }
 
     boolean getInstalled(int userId) {
@@ -326,6 +331,7 @@
 
     void setInstallReason(int installReason, int userId) {
         modifyUserState(userId).installReason = installReason;
+        onChanged();
     }
 
     int getUninstallReason(int userId) {
@@ -334,10 +340,13 @@
 
     void setUninstallReason(@UninstallReason int uninstallReason, int userId) {
         modifyUserState(userId).uninstallReason = uninstallReason;
+        onChanged();
     }
 
     boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
-        return modifyUserState(userId).setOverlayPaths(overlayPaths);
+        boolean returnValue = modifyUserState(userId).setOverlayPaths(overlayPaths);
+        onChanged();
+        return returnValue;
     }
 
     OverlayPaths getOverlayPaths(int userId) {
@@ -346,7 +355,10 @@
 
     boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths,
             int userId) {
-        return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
+        boolean returnValue =  modifyUserState(userId)
+                .setSharedLibraryOverlayPaths(libName, overlayPaths);
+        onChanged();
+        return returnValue;
     }
 
     Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
@@ -395,6 +407,7 @@
 
     void setCeDataInode(long ceDataInode, int userId) {
         modifyUserState(userId).ceDataInode = ceDataInode;
+        onChanged();
     }
 
     boolean getStopped(int userId) {
@@ -403,6 +416,7 @@
 
     void setStopped(boolean stop, int userId) {
         modifyUserState(userId).stopped = stop;
+        onChanged();
     }
 
     boolean getNotLaunched(int userId) {
@@ -411,6 +425,7 @@
 
     void setNotLaunched(boolean stop, int userId) {
         modifyUserState(userId).notLaunched = stop;
+        onChanged();
     }
 
     boolean getHidden(int userId) {
@@ -419,6 +434,7 @@
 
     void setHidden(boolean hidden, int userId) {
         modifyUserState(userId).hidden = hidden;
+        onChanged();
     }
 
     int getDistractionFlags(int userId) {
@@ -427,6 +443,7 @@
 
     void setDistractionFlags(int distractionFlags, int userId) {
         modifyUserState(userId).distractionFlags = distractionFlags;
+        onChanged();
     }
 
     boolean getSuspended(int userId) {
@@ -487,6 +504,7 @@
 
     void setInstantApp(boolean instantApp, int userId) {
         modifyUserState(userId).instantApp = instantApp;
+        onChanged();
     }
 
     boolean getVirtulalPreload(int userId) {
@@ -495,6 +513,7 @@
 
     void setVirtualPreload(boolean virtualPreload, int userId) {
         modifyUserState(userId).virtualPreload = virtualPreload;
+        onChanged();
     }
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
@@ -547,20 +566,24 @@
 
     void setEnabledComponents(ArraySet<String> components, int userId) {
         modifyUserState(userId).enabledComponents = components;
+        onChanged();
     }
 
     void setDisabledComponents(ArraySet<String> components, int userId) {
         modifyUserState(userId).disabledComponents = components;
+        onChanged();
     }
 
     void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
         modifyUserState(userId).enabledComponents = components != null
                 ? new ArraySet<String>(components) : null;
+        onChanged();
     }
 
     void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
         modifyUserState(userId).disabledComponents = components != null
                 ? new ArraySet<String>(components) : null;
+        onChanged();
     }
 
     PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
@@ -582,10 +605,12 @@
 
     void addDisabledComponent(String componentClassName, int userId) {
         modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
+        onChanged();
     }
 
     void addEnabledComponent(String componentClassName, int userId) {
         modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
+        onChanged();
     }
 
     boolean enableComponentLPw(String componentClassName, int userId) {
@@ -593,6 +618,9 @@
         boolean changed = state.disabledComponents != null
                 ? state.disabledComponents.remove(componentClassName) : false;
         changed |= state.enabledComponents.add(componentClassName);
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
@@ -601,6 +629,9 @@
         boolean changed = state.enabledComponents != null
                 ? state.enabledComponents.remove(componentClassName) : false;
         changed |= state.disabledComponents.add(componentClassName);
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
@@ -610,6 +641,9 @@
                 ? state.disabledComponents.remove(componentClassName) : false;
         changed |= state.enabledComponents != null
                 ? state.enabledComponents.remove(componentClassName) : false;
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
@@ -701,6 +735,7 @@
     PackageSettingBase setPath(@NonNull File path) {
         this.mPath = path;
         this.mPathString = path.toString();
+        onChanged();
         return this;
     }
 
@@ -722,7 +757,9 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
             @Nullable String label, @Nullable Integer icon, @UserIdInt int userId) {
-        return modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+        boolean returnValue = modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+        onChanged();
+        return returnValue;
     }
 
     /**
@@ -732,6 +769,7 @@
      */
     public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
         modifyUserState(userId).resetOverrideComponentLabelIcon();
+        onChanged();
     }
 
     /**
@@ -741,6 +779,7 @@
      */
     public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) {
         modifyUserState(userId).splashScreenTheme = themeName;
+        onChanged();
     }
 
     /**
@@ -776,6 +815,7 @@
      */
     public void setStatesOnCommit() {
         incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
+        onChanged();
     }
 
     /**
@@ -783,6 +823,7 @@
      */
     public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
         incrementalStates.setCallback(callback);
+        onChanged();
     }
 
     /**
@@ -791,6 +832,7 @@
      */
     public void setLoadingProgress(float progress) {
         incrementalStates.setProgress(progress);
+        onChanged();
     }
 
     public long getFirstInstallTime() {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index e85fa23..0f4f58b 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -228,17 +228,20 @@
 
             Vibration.Status status = null;
             while (!mStepQueue.isEmpty()) {
-                long waitTime = mStepQueue.calculateWaitTime();
-                if (waitTime <= 0) {
-                    mStepQueue.consumeNext();
-                } else {
-                    synchronized (mLock) {
+                long waitTime;
+                synchronized (mLock) {
+                    waitTime = mStepQueue.calculateWaitTime();
+                    if (waitTime > 0) {
                         try {
                             mLock.wait(waitTime);
                         } catch (InterruptedException e) {
                         }
                     }
                 }
+                // If we waited, the queue may have changed, so let the loop run again.
+                if (waitTime <= 0) {
+                    mStepQueue.consumeNext();
+                }
                 Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED
                         : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
                 if (status == null && currentStatus != Vibration.Status.RUNNING) {
@@ -387,15 +390,13 @@
         }
 
         /** Returns the time in millis to wait before calling {@link #consumeNext()}. */
+        @GuardedBy("mLock")
         public long calculateWaitTime() {
-            Step nextStep;
-            synchronized (mLock) {
-                if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
-                    // Steps anticipated by vibrator complete callback should be played right away.
-                    return 0;
-                }
-                nextStep = mNextSteps.peek();
+            if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+                // Steps anticipated by vibrator complete callback should be played right away.
+                return 0;
             }
+            Step nextStep = mNextSteps.peek();
             return nextStep == null ? 0 : nextStep.calculateWaitTime();
         }
 
@@ -603,7 +604,10 @@
             return false;
         }
 
-        /** Returns the time in millis to wait before playing this step. */
+        /**
+         * Returns the time in millis to wait before playing this step. This is performed
+         * while holding the queue lock, so should not rely on potentially slow operations.
+         */
         public long calculateWaitTime() {
             if (startTime == Long.MAX_VALUE) {
                 // This step don't have a predefined start time, it's just marked to be executed
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c7ae9fd..1b799df 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -729,13 +729,20 @@
 
         // When switching the app task, we keep the IME window visibility for better
         // transitioning experiences.
-        // However, in case IME created a child window without dismissing during the task
-        // switching to keep the window focus because IME window has higher window hierarchy,
-        // we don't give it focus if the next IME layering target doesn't request IME visible.
+        // However, in case IME created a child window or the IME selection dialog without
+        // dismissing during the task switching to keep the window focus because IME window has
+        // higher window hierarchy, we don't give it focus if the next IME layering target
+        // doesn't request IME visible.
         if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
                 || !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME))) {
             return false;
         }
+        if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
+                && !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME)
+                && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
+                ANIMATION_TYPE_APP_TRANSITION)) {
+            return false;
+        }
 
         final ActivityRecord activity = w.mActivityRecord;
 
@@ -3978,7 +3985,9 @@
         // Update Ime parent when IME insets leash created or the new IME layering target might
         // updated from setImeLayeringTarget, which is the best time that default IME visibility
         // has been settled down after IME control target changed.
-        if (prevImeControlTarget != mImeControlTarget || forceUpdateImeParent) {
+        final boolean imeParentChanged =
+                prevImeControlTarget != mImeControlTarget || forceUpdateImeParent;
+        if (imeParentChanged) {
             updateImeParent();
         }
 
@@ -3986,7 +3995,7 @@
         final IBinder token = win != null ? win.mClient.asBinder() : null;
         // Note: not allowed to call into IMMS with the WM lock held, hence the post.
         mWmService.mH.post(() ->
-                InputMethodManagerInternal.get().reportImeControl(token)
+                InputMethodManagerInternal.get().reportImeControl(token, imeParentChanged)
         );
     }
 
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index 53b6b41..eab3f10 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -66,10 +66,8 @@
         } else {
             mNavBarToken = null;
         }
-        // Do not fade notification shade when running fixed rotation (not frozen) because it may
-        // need to animate with the launching app.
-        final WindowState notificationShade = mFrozenTimeoutRunnable == null
-                ? displayPolicy.getNotificationShade() : null;
+        // Collect the target windows to fade out. The display won't wait for them to unfreeze.
+        final WindowState notificationShade = displayPolicy.getNotificationShade();
         displayContent.forAllWindows(w -> {
             if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
                     && !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 672ebf1..fe1020c 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -374,7 +374,9 @@
                 // can be committed before KEYGUARD_OCCLUDE transition is handled.
                 // Set mRequestForceTransition flag to make sure that the app transition animation
                 // is applied for such case.
-                if (topActivity != null) {
+                // TODO(b/194243906): Fix this before enabling the remote keyguard animation.
+                if (WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation
+                        && topActivity != null) {
                     topActivity.mRequestForceTransition = true;
                 }
                 updateKeyguardSleepToken(DEFAULT_DISPLAY);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 7bf0bb8..57ee7aa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -145,7 +145,6 @@
         }
         mContext = InstrumentationRegistry.getContext();
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
         mCaptor = new EventCaptor();
         mHandler = new TestHandler();
         mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
index c12eb32..e98a4dd 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
@@ -64,12 +64,16 @@
                 .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
-                eq(AppOpsManager.MODE_ALLOWED));
+                eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED),
+                eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE),
+                eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE));
         inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
                 .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
-                eq(AppOpsManager.MODE_ALLOWED));
+                eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED),
+                eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE),
+                eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE));
 
         // Stop watching
         appOpsManager.stopWatchingStarted(listener);
@@ -94,7 +98,9 @@
                 .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
-                eq(AppOpsManager.MODE_ALLOWED));
+                eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED),
+                eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE),
+                eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE));
         verifyNoMoreInteractions(listener);
 
         // Finish up
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 8592166a..f4d1499 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -378,6 +378,11 @@
         protected void handleLifecycleAfterAuth(boolean authenticated) {
 
         }
+
+        @Override
+        public boolean wasUserDetected() {
+            return false;
+        }
     }
 
     private static class TestAuthenticationClient extends AuthenticationClient<Object> {
@@ -407,6 +412,11 @@
         protected void handleLifecycleAfterAuth(boolean authenticated) {
 
         }
+
+        @Override
+        public boolean wasUserDetected() {
+            return false;
+        }
     }
 
     private static class TestClientMonitor2 extends TestClientMonitor {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
index f1adcae..bfb0be7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.withSettings;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
 import android.os.Handler;
 import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
@@ -61,6 +62,8 @@
     private Context mContext;
     @Mock
     private CoexCoordinator.Callback mCallback;
+    @Mock
+    private CoexCoordinator.ErrorCallback mErrorCallback;
 
     @Before
     public void setUp() {
@@ -70,6 +73,7 @@
 
         mCoexCoordinator = CoexCoordinator.getInstance();
         mCoexCoordinator.setAdvancedLogicEnabled(true);
+        mCoexCoordinator.setFaceHapticDisabledWhenNonBypass(true);
     }
 
     @Test
@@ -151,12 +155,76 @@
 
         mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
                 mCallback);
-        verify(mCallback).sendHapticFeedback();
+        // Haptics tested in #testKeyguard_bypass_haptics. Let's leave this commented out (instead
+        // of removed) to keep this context.
+        // verify(mCallback).sendHapticFeedback();
         verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
         verify(mCallback).handleLifecycleAfterAuth();
     }
 
     @Test
+    public void testKeyguard_faceAuthSuccess_nonBypass_udfpsRunning_noHaptics() {
+        testKeyguard_bypass_haptics(false /* bypassEnabled */,
+                true /* faceAccepted */,
+                false /* shouldReceiveHaptics */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthReject_nonBypass_udfpsRunning_noHaptics() {
+        testKeyguard_bypass_haptics(false /* bypassEnabled */,
+                false /* faceAccepted */,
+                false /* shouldReceiveHaptics */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthSuccess_bypass_udfpsRunning_haptics() {
+        testKeyguard_bypass_haptics(true /* bypassEnabled */,
+                true /* faceAccepted */,
+                true /* shouldReceiveHaptics */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthReject_bypass_udfpsRunning_haptics() {
+        testKeyguard_bypass_haptics(true /* bypassEnabled */,
+                false /* faceAccepted */,
+                true /* shouldReceiveHaptics */);
+    }
+
+    private void testKeyguard_bypass_haptics(boolean bypassEnabled, boolean faceAccepted,
+            boolean shouldReceiveHaptics) {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+        when(faceClient.isKeyguard()).thenReturn(true);
+        when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+
+        AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+                withSettings().extraInterfaces(Udfps.class));
+        when(udfpsClient.isKeyguard()).thenReturn(true);
+        when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+        if (faceAccepted) {
+            mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
+                    mCallback);
+        } else {
+            mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+                    LockoutTracker.LOCKOUT_NONE, mCallback);
+        }
+
+        if (shouldReceiveHaptics) {
+            verify(mCallback).sendHapticFeedback();
+        } else {
+            verify(mCallback, never()).sendHapticFeedback();
+        }
+
+        verify(mCallback).sendAuthenticationResult(eq(faceAccepted) /* addAuthTokenIfStrong */);
+        verify(mCallback).handleLifecycleAfterAuth();
+    }
+
+    @Test
     public void testKeyguard_faceAuth_udfpsTouching_faceSuccess_thenUdfpsRejectedWithinBounds() {
         testKeyguard_faceAuth_udfpsTouching_faceSuccess(false /* thenUdfpsAccepted */,
                 0 /* udfpsRejectedAfterMs */);
@@ -190,13 +258,16 @@
         mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
         mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
 
+        // For easier reading
+        final CoexCoordinator.Callback faceCallback = mCallback;
+
         mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
-                mCallback);
-        verify(mCallback, never()).sendHapticFeedback();
-        verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
+                faceCallback);
+        verify(faceCallback, never()).sendHapticFeedback();
+        verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
         // CoexCoordinator requests the system to hold onto this AuthenticationClient until
         // UDFPS result is known
-        verify(mCallback, never()).handleLifecycleAfterAuth();
+        verify(faceCallback, never()).handleLifecycleAfterAuth();
 
         // Reset the mock
         CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
@@ -209,6 +280,8 @@
             verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
             verify(udfpsCallback).handleLifecycleAfterAuth();
 
+            verify(faceCallback).sendAuthenticationCanceled();
+
             assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
         } else {
             mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient,
@@ -216,16 +289,16 @@
             if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
                 verify(udfpsCallback, never()).sendHapticFeedback();
 
-                verify(mCallback).sendHapticFeedback();
-                verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
-                verify(mCallback).handleLifecycleAfterAuth();
+                verify(faceCallback).sendHapticFeedback();
+                verify(faceCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
+                verify(faceCallback).handleLifecycleAfterAuth();
 
                 assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
             } else {
                 assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
 
-                verify(mCallback, never()).sendHapticFeedback();
-                verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
+                verify(faceCallback, never()).sendHapticFeedback();
+                verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
 
                 verify(udfpsCallback).sendHapticFeedback();
                 verify(udfpsCallback)
@@ -294,12 +367,13 @@
     }
 
     @Test
-    public void testKeyguard_udfpsRejected_thenFaceRejected() {
+    public void testKeyguard_udfpsRejected_thenFaceRejected_noKeyguardBypass() {
         mCoexCoordinator.reset();
 
         AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
         when(faceClient.isKeyguard()).thenReturn(true);
         when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+        when(faceClient.isKeyguardBypassEnabled()).thenReturn(false); // TODO: also test "true" case
 
         AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
                 withSettings().extraInterfaces(Udfps.class));
@@ -312,8 +386,9 @@
 
         mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient,
                 LockoutTracker.LOCKOUT_NONE, mCallback);
-        // Client becomes paused, but finger does not necessarily lift, since we suppress the haptic
-        when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED_PAUSED);
+        // Auth was attempted
+        when(udfpsClient.getState())
+                .thenReturn(AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED);
         verify(mCallback, never()).sendHapticFeedback();
         verify(mCallback).handleLifecycleAfterAuth();
 
@@ -418,4 +493,82 @@
         verify(callback).handleLifecycleAfterAuth();
         verify(successfulAuths).remove(eq(auth));
     }
+
+    @Test
+    public void testBiometricPrompt_FaceError() {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> client = mock(AuthenticationClient.class);
+        when(client.isBiometricPrompt()).thenReturn(true);
+        when(client.wasAuthAttempted()).thenReturn(true);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+
+        mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                mErrorCallback);
+        verify(mErrorCallback).sendHapticFeedback();
+    }
+
+    @Test
+    public void testKeyguard_faceAuthOnly_errorWhenBypassEnabled() {
+        testKeyguard_faceAuthOnly(true /* bypassEnabled */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthOnly_errorWhenBypassDisabled() {
+        testKeyguard_faceAuthOnly(false /* bypassEnabled */);
+    }
+
+    private void testKeyguard_faceAuthOnly(boolean bypassEnabled) {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> client = mock(AuthenticationClient.class);
+        when(client.isKeyguard()).thenReturn(true);
+        when(client.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+        when(client.wasAuthAttempted()).thenReturn(true);
+        when(client.wasUserDetected()).thenReturn(true);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+
+        mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                mErrorCallback);
+        verify(mErrorCallback).sendHapticFeedback();
+    }
+
+    @Test
+    public void testKeyguard_coex_faceErrorWhenBypassEnabled() {
+        testKeyguard_coex_faceError(true /* bypassEnabled */);
+    }
+
+    @Test
+    public void testKeyguard_coex_faceErrorWhenBypassDisabled() {
+        testKeyguard_coex_faceError(false /* bypassEnabled */);
+    }
+
+    private void testKeyguard_coex_faceError(boolean bypassEnabled) {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+        when(faceClient.isKeyguard()).thenReturn(true);
+        when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+        when(faceClient.wasAuthAttempted()).thenReturn(true);
+        when(faceClient.wasUserDetected()).thenReturn(true);
+
+        AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+                withSettings().extraInterfaces(Udfps.class));
+        when(udfpsClient.isKeyguard()).thenReturn(true);
+        when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+        mCoexCoordinator.onAuthenticationError(faceClient,
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
+
+        if (bypassEnabled) {
+            verify(mErrorCallback).sendHapticFeedback();
+        } else {
+            verify(mErrorCallback, never()).sendHapticFeedback();
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index a205a1d..1ac28ab 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -1340,11 +1340,19 @@
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
         director.start(createMockSensorManager());
 
-        ArgumentCaptor<ProximityActiveListener> captor =
+        ArgumentCaptor<ProximityActiveListener> ProximityCaptor =
                 ArgumentCaptor.forClass(ProximityActiveListener.class);
         verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class),
-                captor.capture());
-        ProximityActiveListener listener = captor.getValue();
+                ProximityCaptor.capture());
+        ProximityActiveListener proximityListener = ProximityCaptor.getValue();
+
+        ArgumentCaptor<DisplayListener> DisplayCaptor =
+                ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(DisplayCaptor.capture(), any(Handler.class),
+                eq(DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+        DisplayListener displayListener = DisplayCaptor.getValue();
 
         // Verify that there is no proximity vote initially
         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
@@ -1353,13 +1361,38 @@
         when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null),
                   eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60));
 
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(false);
+
         // Set the proximity to active and verify that we added a vote.
-        listener.onProximityActive(true);
+        proximityListener.onProximityActive(true);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Set the display state to doze and verify that the vote is gone
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(true);
+        displayListener.onDisplayAdded(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertNull(vote);
+
+        // Set the display state to on and verify that we added the vote back.
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(false);
+        displayListener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Set the display state to doze and verify that the vote is gone
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(true);
+        displayListener.onDisplayAdded(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertNull(vote);
+
+        // Remove the display to cause the doze state to be removed
+        displayListener.onDisplayRemoved(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
         assertVoteForRefreshRate(vote, 60.f);
 
         // Turn prox off and verify vote is gone.
-        listener.onProximityActive(false);
+        proximityListener.onProximityActive(false);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
         assertNull(vote);
     }
@@ -1710,6 +1743,11 @@
             return null;
         }
 
+        @Override
+        public boolean isDozeState(Display d) {
+            return false;
+        }
+
         void notifyPeakRefreshRateChanged() {
             if (mPeakRefreshRateObserver != null) {
                 mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 61b5c2b..4cc4d55 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -83,7 +83,7 @@
 @Presubmit
 public class VibrationThreadTest {
 
-    private static final int TEST_TIMEOUT_MILLIS = 1_000;
+    private static final int TEST_TIMEOUT_MILLIS = 900;
     private static final int UID = Process.ROOT_UID;
     private static final int VIBRATOR_ID = 1;
     private static final String PACKAGE_NAME = "package";
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 745793c..12fc2f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -50,6 +50,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
@@ -2201,6 +2202,34 @@
         assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
     }
 
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
+        final WindowState imeMenuDialog =
+                createWindow(mImeWindow, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
+        makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
+        assertTrue(imeMenuDialog.canReceiveKeys());
+        mDisplayContent.setInputMethodWindowLocked(mImeWindow);
+
+        // Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        mDisplayContent.setImeLayeringTarget(imeAppTarget);
+        spyOn(imeAppTarget);
+        doReturn(true).when(imeAppTarget).getRequestedVisibility(ITYPE_IME);
+        assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+
+        // Verify imeMenuDialog doesn't be focused window if the next IME target does not
+        // request IME visible.
+        final WindowState nextImeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
+        spyOn(nextImeAppTarget);
+        doReturn(true).when(nextImeAppTarget).isAnimating(PARENTS | TRANSITION,
+                ANIMATION_TYPE_APP_TRANSITION);
+        mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
+        assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+    }
+
     private void removeRootTaskTests(Runnable runnable) {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 965f126..734172f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -36,6 +36,7 @@
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.media.AudioFormat;
+import android.media.AudioManagerInternal;
 import android.media.permission.Identity;
 import android.media.permission.PermissionUtil;
 import android.os.Binder;
@@ -44,6 +45,7 @@
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SharedMemory;
@@ -275,6 +277,7 @@
             LocalServices.getService(PermissionManagerServiceInternal.class)
                     .setHotwordDetectionServiceProvider(null);
             mIdentity = null;
+            updateServiceUidForAudioPolicy(Process.INVALID_UID);
         }
         mCancellationTaskFuture.cancel(/* may interrupt */ true);
         if (mAudioFlinger != null) {
@@ -893,6 +896,8 @@
         connection.run(service -> service.ping(new IRemoteCallback.Stub() {
             @Override
             public void sendResult(Bundle bundle) throws RemoteException {
+                // TODO: Exit if the service has been unbound already (though there's a very low
+                // chance this happens).
                 if (DEBUG) {
                     Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid());
                 }
@@ -902,10 +907,21 @@
                 LocalServices.getService(PermissionManagerServiceInternal.class)
                         .setHotwordDetectionServiceProvider(() -> uid);
                 mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid);
+                updateServiceUidForAudioPolicy(uid);
             }
         }));
     }
 
+    private void updateServiceUidForAudioPolicy(int uid) {
+        mScheduledExecutorService.execute(() -> {
+            final AudioManagerInternal audioManager =
+                    LocalServices.getService(AudioManagerInternal.class);
+            if (audioManager != null) {
+                audioManager.setHotwordDetectionServiceUid(uid);
+            }
+        });
+    }
+
     private static void bestEffortClose(Closeable closeable) {
         try {
             closeable.close();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
index b9e1fcd..68b2e61 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
@@ -29,7 +29,6 @@
 import android.media.permission.PermissionUtil;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceSpecificException;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -117,8 +116,8 @@
 
     /**
      * Throws a {@link SecurityException} if originator permanently doesn't have the given
-     * permission, or a {@link ServiceSpecificException} with a {@link
-     * #TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission.
+     * permission.
+     * Soft (temporary) denials are considered OK for preflight purposes.
      *
      * @param context    A {@link Context}, used for permission checks.
      * @param identity   The identity to check.
@@ -130,15 +129,12 @@
                 permission);
         switch (status) {
             case PermissionChecker.PERMISSION_GRANTED:
+            case PermissionChecker.PERMISSION_SOFT_DENIED:
                 return;
             case PermissionChecker.PERMISSION_HARD_DENIED:
                 throw new SecurityException(
                         TextUtils.formatSimple("Failed to obtain permission %s for identity %s",
                                 permission, toString(identity)));
-            case PermissionChecker.PERMISSION_SOFT_DENIED:
-                throw new ServiceSpecificException(TEMPORARY_PERMISSION_DENIED,
-                        TextUtils.formatSimple("Failed to obtain permission %s for identity %s",
-                                permission, toString(identity)));
             default:
                 throw new RuntimeException("Unexpected permission check result.");
         }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 71541ad..9ea2b7b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1896,17 +1896,19 @@
 
                         String serviceComponentName = serviceInfo.getComponentName()
                                 .flattenToShortString();
-
-                        String serviceRecognizerName = new ComponentName(pkg,
-                                voiceInteractionServiceInfo.getRecognitionService())
-                                .flattenToShortString();
+                        if (voiceInteractionServiceInfo.getRecognitionService() == null) {
+                            Slog.e(TAG, "The RecognitionService must be set to avoid boot "
+                                    + "loop on earlier platform version. Also make sure that this "
+                                    + "is a valid RecognitionService when running on Android 11 "
+                                    + "or earlier.");
+                            serviceComponentName = "";
+                        }
 
                         Settings.Secure.putStringForUser(getContext().getContentResolver(),
                                 Settings.Secure.ASSISTANT, serviceComponentName, userId);
                         Settings.Secure.putStringForUser(getContext().getContentResolver(),
                                 Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName,
                                 userId);
-
                         return;
                     }
 
@@ -1947,6 +1949,29 @@
             }
         }
 
+        private void resetServicesIfNoRecognitionService(ComponentName serviceComponent,
+                int userHandle) {
+            for (ResolveInfo resolveInfo : queryInteractorServices(userHandle,
+                    serviceComponent.getPackageName())) {
+                VoiceInteractionServiceInfo serviceInfo =
+                        new VoiceInteractionServiceInfo(
+                                mContext.getPackageManager(),
+                                resolveInfo.serviceInfo);
+                if (!serviceInfo.getSupportsAssist()) {
+                    continue;
+                }
+                if (serviceInfo.getRecognitionService() == null) {
+                    Slog.e(TAG, "The RecognitionService must be set to "
+                            + "avoid boot loop on earlier platform version. "
+                            + "Also make sure that this is a valid "
+                            + "RecognitionService when running on Android 11 "
+                            + "or earlier.");
+                    setCurInteractor(null, userHandle);
+                    resetCurAssistant(userHandle);
+                }
+            }
+        }
+
         PackageMonitor mPackageMonitor = new PackageMonitor() {
             @Override
             public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
@@ -2090,6 +2115,7 @@
 
                         change = isPackageAppearing(curInteractor.getPackageName());
                         if (change != PACKAGE_UNCHANGED) {
+                            resetServicesIfNoRecognitionService(curInteractor, userHandle);
                             // If current interactor is now appearing, for any reason, then
                             // restart our connection with it.
                             if (mImpl != null && curInteractor.getPackageName().equals(
@@ -2112,6 +2138,13 @@
                             initForUser(userHandle);
                             return;
                         }
+                        change = isPackageAppearing(curAssistant.getPackageName());
+                        if (change != PACKAGE_UNCHANGED) {
+                            // It is possible to update Assistant without a voice interactor to one
+                            // with a voice-interactor. We should make sure the recognition service
+                            // is set to avoid boot loop.
+                            resetServicesIfNoRecognitionService(curAssistant, userHandle);
+                        }
                     }
 
                     // There is no interactor, so just deal with a simple recognizer.
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 628c480..4df8a4b 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -4,13 +4,14 @@
 breadley@google.com
 fionaxu@google.com
 jackyu@google.com
-hallliu@google.com
 rgreenwalt@google.com
 tgunn@google.com
 jminjie@google.com
 shuoq@google.com
-refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
-dbright@google.com
 xiaotonj@google.com
+huiwang@google.com
+jayachandranc@google.com
+chinmayd@google.com
+amruthr@google.com
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index c18ab33..f004824 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -193,6 +193,10 @@
         return mDelegateBinder;
     }
 
+    public ISipDelegateStateCallback getStateCallbackBinder() {
+        return mStateBinder;
+    }
+
     private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
         String transactionId = m.getViaBranchParameter();
         SipDelegate d = mDelegate;
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 1f74c09..13ea9973 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.telephony.ims.DelegateMessageCallback;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.DelegateStateCallback;
@@ -33,6 +34,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -49,10 +51,15 @@
 public class SipTransportImplBase {
     private static final String LOG_TAG = "SipTransportIB";
 
-    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
         @Override
         public void binderDied() {
-            mBinderExecutor.execute(() -> binderDiedInternal());
+            // Clean up all binders in this case.
+            mBinderExecutor.execute(() -> binderDiedInternal(null));
+        }
+        @Override
+        public void binderDied(IBinder who) {
+            mBinderExecutor.execute(() -> binderDiedInternal(who));
         }
     };
 
@@ -142,6 +149,7 @@
             ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) {
         SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
         mDelegates.add(wrapper);
+        linkDeathRecipient(wrapper);
         createSipDelegate(subId, r, wrapper, wrapper);
     }
 
@@ -155,6 +163,7 @@
         }
 
         if (result != null) {
+            unlinkDeathRecipient(result);
             mDelegates.remove(result);
             destroySipDelegate(result.getDelegate(), reason);
         } else {
@@ -163,12 +172,37 @@
         }
     }
 
-    private void binderDiedInternal() {
-        for (SipDelegateAidlWrapper w : mDelegates) {
-            destroySipDelegate(w.getDelegate(),
-                    SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+    private void linkDeathRecipient(SipDelegateAidlWrapper w) {
+        try {
+            w.getStateCallbackBinder().asBinder().linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "linkDeathRecipient, remote process already died, cleaning up.");
+            mDeathRecipient.binderDied(w.getStateCallbackBinder().asBinder());
         }
-        mDelegates.clear();
+    }
+
+    private void unlinkDeathRecipient(SipDelegateAidlWrapper w) {
+        try {
+            w.getStateCallbackBinder().asBinder().unlinkToDeath(mDeathRecipient, 0);
+        } catch (NoSuchElementException e) {
+            // Ignore this case.
+        }
+    }
+
+    private void binderDiedInternal(IBinder who) {
+        for (SipDelegateAidlWrapper w : mDelegates) {
+            // If the binder itself was not given from the platform, just clean up all binders.
+            if (who == null || w.getStateCallbackBinder().asBinder().equals(who))  {
+                Log.w(LOG_TAG, "Binder death detected for " + w + ", calling destroy and "
+                        + "removing.");
+                mDelegates.remove(w);
+                destroySipDelegate(w.getDelegate(),
+                        SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+                return;
+            }
+        }
+        Log.w(LOG_TAG, "Binder death detected for IBinder " + who + ", but couldn't find matching "
+                + "SipDelegate");
     }
 
     /**