Merge "Adjust SystemUI PowerModule" 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..dbaf275 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() {
@@ -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/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/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/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/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..9acf9bf 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;
@@ -78,41 +77,20 @@
         SurfaceInfo surfaceInfo = new SurfaceInfo();
         int nativeFormat = SurfaceUtils.getSurfaceFormat(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..8fd9a6a 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((MeteringRectangle[]) value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AF_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAFRegions((MeteringRectangle[]) value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AE_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAERegions((MeteringRectangle[]) value);
+            }
+        });
     }
 
     private boolean setAvailableFormats(int[] value) {
@@ -1777,6 +1806,42 @@
         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(MeteringRectangle[] afRegions) {
+        if (afRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AF_REGIONS_SET, true);
+        setBase(CaptureRequest.CONTROL_AF_REGIONS, afRegions);
+        return true;
+    }
+
+    private <T> boolean setAERegions(MeteringRectangle[] aeRegions) {
+        if (aeRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AE_REGIONS_SET, true);
+        setBase(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
+        return true;
+    }
+
+    private <T> boolean setAWBRegions(MeteringRectangle[] awbRegions) {
+        if (awbRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AWB_REGIONS_SET, true);
+        setBase(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
+        return true;
+    }
+
     private void updateNativeAllocation() {
         long currentBufferSize = nativeGetBufferSize(mMetadataPtr);
 
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index 35b5c15..57d8ded 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.
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/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 55a2052..c9a9e51 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a307369..95c24ad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3507,7 +3507,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/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/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 74fb618..4925209 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) {
@@ -74,6 +75,12 @@
         return this;
     }
 
+    void end() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.end();
+        }
+    }
+
     @NonNull RippleAnimationSession exit(Canvas canvas) {
         if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas);
         else exitSoftware();
@@ -114,10 +121,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 +156,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 +165,7 @@
         long delay = computeDelay();
         exit.setStartDelay(delay);
         exit.start();
+        mCurrentAnimation = exit;
     }
 
     private void enterHardware(RecordingCanvas canvas) {
@@ -167,6 +178,7 @@
                 mStartTime + MAX_NOISE_PHASE);
         loop.setTarget(canvas);
         startAnimation(expand, loop);
+        mCurrentAnimation = expand;
     }
 
     private void startAnimation(Animator expand, Animator loop) {
@@ -200,6 +212,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..d3cff5c 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -278,6 +278,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 +300,6 @@
             Arrays.fill(ripples, 0, count, null);
         }
         mExitingRipplesCount = 0;
-        mExitingAnimation = true;
         // Always draw an additional "clean" frame after canceling animations.
         invalidateSelf(false);
     }
@@ -714,7 +722,7 @@
         }
 
         cancelExitingRipples();
-        exitPatternedAnimation();
+        endPatternedAnimations();
     }
 
     @Override
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/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 4114879..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/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/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/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-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/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9a9e4a9..23d6ebc 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";
@@ -834,7 +834,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) {
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/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..d122610 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -22,7 +22,6 @@
 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;
@@ -51,17 +50,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
@@ -89,20 +85,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);
@@ -148,13 +139,7 @@
     }
 
     void requestUdfps(boolean request, int color) {
-        if (request) {
-            mUdfpsRequestedColor = color;
-        } else {
-            mUdfpsRequestedColor = -1;
-        }
         mUdfpsRequested = request;
-        updateColor();
     }
 
     void setStatusBarState(int statusBarState) {
@@ -162,31 +147,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 +164,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;
     }
 
@@ -215,6 +185,7 @@
     void onDozeAmountChanged(float linear, float eased) {
         mHintAnimator.cancel();
         mInterpolatedDarkAmount = eased;
+        updateAlpha();
         updateBurnInOffsets();
     }
 
@@ -228,52 +199,21 @@
     /**
      * 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 +221,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..58f1254 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 
 import android.annotation.NonNull;
+import android.content.res.Configuration;
 import android.hardware.biometrics.BiometricSourceType;
 import android.util.MathUtils;
 import android.view.MotionEvent;
@@ -36,6 +37,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;
@@ -57,6 +59,7 @@
     @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;
@@ -87,6 +90,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 +98,7 @@
         mExecutor = mainDelayableExecutor;
         mKeyguardViewMediator = keyguardViewMediator;
         mLockScreenShadeTransitionController = transitionController;
+        mConfigurationController = configurationController;
         mUdfpsController = udfpsController;
     }
 
@@ -120,6 +125,7 @@
         mQsExpanded = mKeyguardViewManager.isQsExpanded();
         mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
         mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
+        mConfigurationController.addCallback(mConfigurationListener);
         updateAlpha();
         updatePauseAuth();
 
@@ -136,6 +142,7 @@
         mStatusBarStateController.removeCallback(mStateListener);
         mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+        mConfigurationController.removeCallback(mConfigurationListener);
         if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
@@ -158,7 +165,6 @@
         pw.println("mAlpha=" + mView.getAlpha());
         pw.println("mUdfpsRequested=" + mUdfpsRequested);
         pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
-        pw.println("mView.mUdfpsRequestedColor=" + mView.mUdfpsRequestedColor);
     }
 
     /**
@@ -173,12 +179,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;
@@ -366,7 +377,7 @@
 
                 @Override
                 public boolean isAnimating() {
-                    return mView.isAnimating();
+                    return false;
                 }
 
                 @Override
@@ -407,4 +418,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/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/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..c9230d6 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
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/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/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/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/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/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/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/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/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..6fb9e58 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/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/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c7ae9fd..4c11422 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -729,12 +729,19 @@
 
         // 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.
-        if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
+        // 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 && (mImeLayeringTarget == null
                 || !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME))) {
-            return false;
+            if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+                return false;
+            }
+
+            if (w.isChildWindow()) {
+                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/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/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 745793c..c60b8dc 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,31 @@
         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");
+        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,