Merge "DO NOT MERGE Hold onto NotificationListener when reconnecting notifications UI." into qt-qpr1-dev
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4b4b73d..5d240f1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -485,6 +485,15 @@
         // CarFacetButtonController was reset therefore we need to re-add the status bar elements
         // to the controller.
         mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
+
+        // Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the
+        // selection state that reflects the current task stack.
+        try {
+            mCarFacetButtonController.taskChanged(
+                    ActivityTaskManager.getService().getAllStackInfos());
+        } catch (Exception e) {
+            Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+        }
     }
 
     private void addTemperatureViewToController(View v) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index f91c90e..b87844e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -26,8 +26,11 @@
 import android.car.Car;
 import android.car.Car.CarServiceLifecycleListener;
 import android.car.media.CarAudioManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Color;
@@ -39,6 +42,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -175,6 +179,17 @@
         mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
     };
 
+    private final BroadcastReceiver mHomeButtonPressedBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
+                return;
+            }
+
+            dismiss(Events.DISMISS_REASON_VOLUME_CONTROLLER);
+        }
+    };
+
     public CarVolumeDialogImpl(Context context) {
         mContext = context;
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
@@ -204,6 +219,10 @@
     public void init(int windowType, Callback callback) {
         initDialog();
 
+        mContext.registerReceiverAsUser(mHomeButtonPressedBroadcastReceiver, UserHandle.CURRENT,
+                new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* broadcastPermission= */
+                null, /* scheduler= */ null);
+
         ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
                 .addListener(mCarServiceLifecycleListener);
     }
@@ -212,6 +231,8 @@
     public void destroy() {
         mHandler.removeCallbacksAndMessages(/* token= */ null);
 
+        mContext.unregisterReceiver(mHomeButtonPressedBroadcastReceiver);
+
         cleanupAudioManager();
     }
 
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 4fae3c5..f8db97d 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -24,7 +24,6 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/status_bar_height"
     android:id="@+id/status_bar"
-    android:background="@drawable/system_bar_background"
     android:orientation="vertical"
     android:focusable="false"
     android:descendantFocusability="afterDescendants"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index a9149300..9d56e08 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -54,7 +54,8 @@
     <FrameLayout
         android:id="@+id/status_bar_container"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:background="@drawable/system_bar_background" />
 
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 78318cb..6758efa 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -201,6 +201,14 @@
          always-on display) -->
     <string name="doze_brightness_sensor_type" translatable="false"></string>
 
+    <!-- Override value to use for proximity sensor.  -->
+    <string name="proximity_sensor_type" translatable="false">@string/doze_brightness_sensor_type</string>
+
+    <!-- If using proximity_sensor_type, specifies a threshold value to distinguish near and
+         far break points. A sensor value less than or equal to this is considered "near". -->
+    <item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen">
+        0</item>
+
     <!-- Doze: pulse parameter - how long does it take to fade in? -->
     <integer name="doze_pulse_duration_in">130</integer>
 
@@ -480,4 +488,24 @@
     <!-- Preferred refresh rate at keyguard, if supported by the display -->
     <integer name="config_keyguardRefreshRate">-1</integer>
 
+    <!-- Respect the drawable/rounded.xml that allow to customize as multiple radius corner path -->
+    <bool name="config_roundedCornerMultipleRadius">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
+      SystemUI will draw this "protection path" instead of the display cutout path that is normally
+      used for anti-aliasing.
+
+      This path will only be drawn when the front-facing camera turns on, otherwise the main
+      DisplayCutout path will be rendered
+       -->
+    <string translatable="false" name="config_frontBuiltInDisplayCutoutProtection"></string>
+
+    <!--  ID for the camera that needs extra protection -->
+    <string translatable="false" name="config_protectedCameraId"></string>
+
+    <!--  Flag to turn on the rendering of the above path or not  -->
+    <bool name="config_enableDisplayCutoutProtection">false</bool>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
new file mode 100644
index 0000000..24fa91b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui
+
+import android.content.Context
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.hardware.camera2.CameraManager
+import android.util.PathParser
+import java.util.concurrent.Executor
+
+import kotlin.math.roundToInt
+
+const val TAG = "CameraOpTransitionController"
+
+/**
+ * Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
+ * protection around a display cutout based on config_frontBuiltInDisplayCutoutProtection and
+ * config_enableDisplayCutoutProtection
+ */
+class CameraAvailabilityListener(
+    private val cameraManager: CameraManager,
+    private val cutoutProtectionPath: Path,
+    private val targetCameraId: String,
+    private val executor: Executor
+) {
+    private var cutoutBounds = Rect()
+    private val listeners = mutableListOf<CameraTransitionCallback>()
+    private val availabilityCallback: CameraManager.AvailabilityCallback =
+            object : CameraManager.AvailabilityCallback() {
+                override fun onCameraAvailable(cameraId: String) {
+                    if (targetCameraId == cameraId) {
+                        notifyCameraInactive()
+                    }
+                }
+
+                override fun onCameraUnavailable(cameraId: String) {
+                    if (targetCameraId == cameraId) {
+                        notifyCameraActive()
+                    }
+                }
+    }
+
+    init {
+        val computed = RectF()
+        cutoutProtectionPath.computeBounds(computed, false /* unused */)
+        cutoutBounds.set(
+                computed.left.roundToInt(),
+                computed.top.roundToInt(),
+                computed.right.roundToInt(),
+                computed.bottom.roundToInt())
+    }
+
+    /**
+     * Start listening for availability events, and maybe notify listeners
+     *
+     * @return true if we started listening
+     */
+    fun startListening() {
+        registerCameraListener()
+    }
+
+    fun stop() {
+        unregisterCameraListener()
+    }
+
+    fun addTransitionCallback(callback: CameraTransitionCallback) {
+        listeners.add(callback)
+    }
+
+    fun removeTransitionCallback(callback: CameraTransitionCallback) {
+        listeners.remove(callback)
+    }
+
+    private fun registerCameraListener() {
+        cameraManager.registerAvailabilityCallback(executor, availabilityCallback)
+    }
+
+    private fun unregisterCameraListener() {
+        cameraManager.unregisterAvailabilityCallback(availabilityCallback)
+    }
+
+    private fun notifyCameraActive() {
+        listeners.forEach { it.onApplyCameraProtection(cutoutProtectionPath, cutoutBounds) }
+    }
+
+    private fun notifyCameraInactive() {
+        listeners.forEach { it.onHideCameraProtection() }
+    }
+
+    /**
+     * Callbacks to tell a listener that a relevant camera turned on and off.
+     */
+    interface CameraTransitionCallback {
+        fun onApplyCameraProtection(protectionPath: Path, bounds: Rect)
+        fun onHideCameraProtection()
+    }
+
+    companion object Factory {
+        fun build(context: Context, executor: Executor): CameraAvailabilityListener {
+            val manager = context
+                    .getSystemService(Context.CAMERA_SERVICE) as CameraManager
+            val res = context.resources
+            val pathString = res.getString(R.string.config_frontBuiltInDisplayCutoutProtection)
+            val cameraId = res.getString(R.string.config_protectedCameraId)
+
+            return CameraAvailabilityListener(
+                    manager, pathFromString(pathString), cameraId, executor)
+        }
+
+        private fun pathFromString(pathString: String): Path {
+            val spec = pathString.trim()
+            val p: Path
+            try {
+                p = PathParser.createPathFromPathData(spec)
+            } catch (e: Throwable) {
+                throw IllegalArgumentException("Invalid protection path", e)
+            }
+
+            return p
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index f38b4f2..3a8524a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -29,6 +29,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.Dimension;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.Fragment;
 import android.content.BroadcastReceiver;
@@ -37,6 +38,7 @@
 import android.content.IntentFilter;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
@@ -45,6 +47,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.drawable.VectorDrawable;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -105,9 +108,11 @@
     private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
             SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
     private static final boolean VERBOSE = false;
+    private static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
 
     private DisplayManager mDisplayManager;
     private DisplayManager.DisplayListener mDisplayListener;
+    private CameraAvailabilityListener mCameraListener;
 
     @VisibleForTesting
     protected int mRoundedDefault;
@@ -129,6 +134,26 @@
     private boolean mAssistHintBlocked = false;
     private boolean mIsReceivingNavBarColor = false;
     private boolean mInGesturalMode;
+    private boolean mIsRoundedCornerMultipleRadius;
+
+    private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
+            new CameraAvailabilityListener.CameraTransitionCallback() {
+        @Override
+        public void onApplyCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
+            // Show the extra protection around the front facing camera if necessary
+            mCutoutTop.setProtection(protectionPath, bounds);
+            mCutoutTop.setShowProtection(true);
+            mCutoutBottom.setProtection(protectionPath, bounds);
+            mCutoutBottom.setShowProtection(true);
+        }
+
+        @Override
+        public void onHideCameraProtection() {
+            // Go back to the regular anti-aliasing
+            mCutoutTop.setShowProtection(false);
+            mCutoutBottom.setShowProtection(false);
+        }
+    };
 
     /**
      * Converts a set of {@link Rect}s into a {@link Region}
@@ -323,9 +348,12 @@
     private void startOnScreenDecorationsThread() {
         mRotation = RotationUtils.getExactRotation(mContext);
         mWindowManager = mContext.getSystemService(WindowManager.class);
+        mIsRoundedCornerMultipleRadius = mContext.getResources().getBoolean(
+                R.bool.config_roundedCornerMultipleRadius);
         updateRoundedCornerRadii();
         if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) {
             setupDecorations();
+            setupCameraListener();
         }
 
         mDisplayListener = new DisplayManager.DisplayListener() {
@@ -441,6 +469,16 @@
                 new ValidatingPreDrawListener(mBottomOverlay));
     }
 
+    private void setupCameraListener() {
+        Resources res = mContext.getResources();
+        boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
+        if (enabled) {
+            mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mHandler::post);
+            mCameraListener.addTransitionCallback(mCameraTransitionCallback);
+            mCameraListener.startListening();
+        }
+    }
+
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -457,6 +495,9 @@
 
     private void updateColorInversion(int colorsInvertedValue) {
         int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
+        if (DEBUG_COLOR) {
+            tint = Color.RED;
+        }
         ColorStateList tintList = ColorStateList.valueOf(tint);
         ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
         ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
@@ -528,17 +569,24 @@
                 com.android.internal.R.dimen.rounded_corner_radius_top);
         final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.rounded_corner_radius_bottom);
-
         final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
                 || mRoundedDefaultBottom != newRoundedDefaultBottom
                 || mRoundedDefaultTop != newRoundedDefaultTop;
 
         if (roundedCornersChanged) {
-            mRoundedDefault = newRoundedDefault;
-            mRoundedDefaultTop = newRoundedDefaultTop;
-            mRoundedDefaultBottom = newRoundedDefaultBottom;
-            onTuningChanged(SIZE, null);
+            // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
+            // max(width, height) size of drawable/rounded.xml instead of rounded_corner_radius
+            if (mIsRoundedCornerMultipleRadius) {
+                final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+                mRoundedDefault = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+                mRoundedDefaultTop = mRoundedDefaultBottom = mRoundedDefault;
+            } else {
+                mRoundedDefault = newRoundedDefault;
+                mRoundedDefaultTop = newRoundedDefaultTop;
+                mRoundedDefaultBottom = newRoundedDefaultBottom;
+            }
         }
+        onTuningChanged(SIZE, null);
     }
 
     private void updateViews() {
@@ -637,7 +685,8 @@
     }
 
     private boolean hasRoundedCorners() {
-        return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
+        return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0
+                || mIsRoundedCornerMultipleRadius;
     }
 
     private boolean shouldDrawCutout() {
@@ -825,6 +874,13 @@
         private final List<Rect> mBounds = new ArrayList();
         private final Rect mBoundingRect = new Rect();
         private final Path mBoundingPath = new Path();
+        // Don't initialize these because they are cached elsewhere and may not exist
+        private Rect mProtectionRect;
+        private Path mProtectionPath;
+        private Rect mTotalBounds = new Rect();
+        // Whether or not to show the cutout protection path
+        private boolean mShowProtection = false;
+
         private final int[] mLocation = new int[2];
         private final boolean mInitialStart;
         private final Runnable mVisibilityChangedListener;
@@ -871,7 +927,13 @@
             super.onDraw(canvas);
             getLocationOnScreen(mLocation);
             canvas.translate(-mLocation[0], -mLocation[1]);
-            if (!mBoundingPath.isEmpty()) {
+
+            if (mShowProtection && !mProtectionRect.isEmpty()) {
+                mPaint.setColor(mColor);
+                mPaint.setStyle(Paint.Style.FILL);
+                mPaint.setAntiAlias(true);
+                canvas.drawPath(mProtectionPath, mPaint);
+            } else if (!mBoundingPath.isEmpty()) {
                 mPaint.setColor(mColor);
                 mPaint.setStyle(Paint.Style.FILL);
                 mPaint.setAntiAlias(true);
@@ -899,6 +961,22 @@
             update();
         }
 
+        void setProtection(Path protectionPath, Rect pathBounds) {
+            mProtectionPath = protectionPath;
+            mProtectionRect = pathBounds;
+        }
+
+        void setShowProtection(boolean shouldShow) {
+            if (mShowProtection == shouldShow) {
+                return;
+            }
+
+            mShowProtection = shouldShow;
+            updateBoundingPath();
+            requestLayout();
+            invalidate();
+        }
+
         private boolean isStart() {
             final boolean flipped = (mRotation == RotationUtils.ROTATION_SEASCAPE
                     || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN);
@@ -945,6 +1023,9 @@
             Matrix m = new Matrix();
             transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
             mBoundingPath.transform(m);
+            if (mProtectionPath != null) {
+                mProtectionPath.transform(m);
+            }
         }
 
         private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
@@ -1002,9 +1083,19 @@
                 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                 return;
             }
-            setMeasuredDimension(
-                    resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
-                    resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
+
+            if (mShowProtection) {
+                // Make sure that our measured height encompases the protection
+                mTotalBounds.union(mBoundingRect);
+                mTotalBounds.union(mProtectionRect);
+                setMeasuredDimension(
+                        resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
+                        resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0));
+            } else {
+                setMeasuredDimension(
+                        resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
+                        resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
+            }
         }
 
         public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 026a625..b7c8f70 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -43,11 +43,11 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.R;
 import com.android.systemui.plugins.SensorManagerPlugin;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.ProximitySensor;
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.io.PrintWriter;
@@ -288,6 +288,7 @@
         final AlarmTimeout mCooldownTimer;
         final AlwaysOnDisplayPolicy mPolicy;
         final Sensor mSensor;
+        private final float mSensorThreshold;
         final boolean mUsingBrightnessSensor;
 
         public ProxSensor(AlwaysOnDisplayPolicy policy) {
@@ -297,11 +298,14 @@
 
             // The default prox sensor can be noisy, so let's use a prox gated brightness sensor
             // if available.
-            Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
-                    mContext.getString(R.string.doze_brightness_sensor_type));
+            Sensor sensor = ProximitySensor.findCustomProxSensor(mContext, mSensorManager);
             mUsingBrightnessSensor = sensor != null;
-            if (sensor == null) {
+            if (mUsingBrightnessSensor) {
+                mSensorThreshold = ProximitySensor.getBrightnessSensorThreshold(
+                        mContext.getResources());
+            } else {
                 sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+                mSensorThreshold = sensor.getMaximumRange();
             }
             mSensor = sensor;
         }
@@ -343,11 +347,9 @@
             if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
 
             if (mUsingBrightnessSensor) {
-                // The custom brightness sensor is gated by the proximity sensor and will return 0
-                // whenever prox is covered.
-                mCurrentlyFar = event.values[0] > 0;
+                mCurrentlyFar = event.values[0] > mSensorThreshold;
             } else {
-                mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
+                mCurrentlyFar = event.values[0] >= mSensorThreshold;
             }
             mProxCallback.accept(mCurrentlyFar);
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 6199a0d..c7a133d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,10 +41,10 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
 import com.android.systemui.Dependency;
-import com.android.systemui.R;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.ProximitySensor;
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.io.PrintWriter;
@@ -431,15 +431,18 @@
         private boolean mFinished;
         private float mMaxRange;
         private boolean mUsingBrightnessSensor;
+        private float mSensorThreshold;
 
         protected abstract void onProximityResult(int result);
 
         public void check() {
             Preconditions.checkState(!mFinished && !mRegistered);
-            Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
-                    mContext.getString(R.string.doze_brightness_sensor_type));
+            Sensor sensor = ProximitySensor.findCustomProxSensor(mContext, mSensorManager);
             mUsingBrightnessSensor = sensor != null;
-            if (sensor == null) {
+            if (mUsingBrightnessSensor) {
+                mSensorThreshold = ProximitySensor.getBrightnessSensorThreshold(
+                        mContext.getResources());
+            } else {
                 sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
             }
             if (sensor == null) {
@@ -473,7 +476,7 @@
                 if (mUsingBrightnessSensor) {
                     // The custom brightness sensor is gated by the proximity sensor and will
                     // return 0 whenever prox is covered.
-                    isNear = event.values[0] == 0;
+                    isNear = event.values[0] <= mSensorThreshold;
                 } else {
                     isNear = event.values[0] < mMaxRange;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 496aa0e..837f90e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -25,9 +25,12 @@
 import android.content.res.Configuration;
 import android.graphics.drawable.Animatable;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.util.SparseArray;
+import android.view.DisplayCutout;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -42,6 +45,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 
 public class QSDetail extends LinearLayout {
 
@@ -142,6 +146,25 @@
         }
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        DisplayCutout cutout = insets.getDisplayCutout();
+        Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
+                cutout, getDisplay());
+        if (padding == null) {
+            mQsDetailHeader.setPaddingRelative(
+                    getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+                    getPaddingTop(),
+                    getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+                    getPaddingBottom()
+            );
+        } else {
+            mQsDetailHeader.setPadding(padding.first, getPaddingTop(),
+                    padding.second, getPaddingBottom());
+        }
+        return super.onApplyWindowInsets(insets);
+    }
+
     private void updateDetailText() {
         mDetailDoneButton.setText(R.string.quick_settings_done);
         mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index a7d5aca..111309f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -29,23 +29,21 @@
     private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK = 0.5f;
     private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK = 0;
 
-    private final PhoneStatusBarView mView;
     private final float mIconAlphaWhenOpaque;
 
-    private View mLeftSide, mStatusIcons, mBattery, mClock;
+    private View mLeftSide, mStatusIcons, mBattery;
     private Animator mCurrentAnimation;
 
-    public PhoneStatusBarTransitions(PhoneStatusBarView view) {
-        super(view, R.drawable.status_background);
-        mView = view;
-        final Resources res = mView.getContext().getResources();
+    /**
+     * @param backgroundView view to apply the background drawable
+     */
+    public PhoneStatusBarTransitions(PhoneStatusBarView statusBarView, View backgroundView) {
+        super(backgroundView, R.drawable.status_background);
+        final Resources res = statusBarView.getContext().getResources();
         mIconAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
-    }
-
-    public void init() {
-        mLeftSide = mView.findViewById(R.id.status_bar_left_side);
-        mStatusIcons = mView.findViewById(R.id.statusIcons);
-        mBattery = mView.findViewById(R.id.battery);
+        mLeftSide = statusBarView.findViewById(R.id.status_bar_left_side);
+        mStatusIcons = statusBarView.findViewById(R.id.statusIcons);
+        mBattery = statusBarView.findViewById(R.id.battery);
         applyModeBackground(-1, getMode(), false /*animate*/);
         applyMode(getMode(), false /*animate*/);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 8efd952..32402e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -59,7 +59,6 @@
     StatusBar mBar;
 
     boolean mIsFullyOpenedPanel = false;
-    private final PhoneStatusBarTransitions mBarTransitions;
     private ScrimController mScrimController;
     private float mMinFraction;
     private Runnable mHideExpandedRunnable = new Runnable() {
@@ -87,14 +86,9 @@
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mBarTransitions = new PhoneStatusBarTransitions(this);
         mCommandQueue = getComponent(context, CommandQueue.class);
     }
 
-    public BarTransitions getBarTransitions() {
-        return mBarTransitions;
-    }
-
     public void setBar(StatusBar bar) {
         mBar = bar;
     }
@@ -105,7 +99,6 @@
 
     @Override
     public void onFinishInflate() {
-        mBarTransitions.init();
         mBattery = findViewById(R.id.battery);
         mCutoutSpace = findViewById(R.id.cutout_space_view);
         mCenterIconSpace = findViewById(R.id.centered_icon_area);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c6dee5e..ea04bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2238,7 +2238,7 @@
     }
 
     protected BarTransitions getStatusBarTransitions() {
-        return mStatusBarView.getBarTransitions();
+        return mStatusBarWindow.getBarTransitions();
     }
 
     protected @TransitionMode int computeBarMode(int oldVis, int newVis) {
@@ -2288,8 +2288,8 @@
     }
 
     private void finishBarAnimations() {
-        if (mStatusBarView != null) {
-            mStatusBarView.getBarTransitions().finishAnimations();
+        if (mStatusBarWindow != null && mStatusBarWindow.getBarTransitions() != null) {
+            mStatusBarWindow.getBarTransitions().finishAnimations();
         }
         mNavigationBarController.finishBarAnimations(mDisplayId);
     }
@@ -2367,8 +2367,8 @@
                 Settings.Global.ZEN_MODE_OFF)));
         pw.print("  mWallpaperSupported= "); pw.println(mWallpaperSupported);
 
-        if (mStatusBarView != null) {
-            dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
+        if (mStatusBarWindow != null) {
+            dumpBarTransitions(pw, "mStatusBarWindow", mStatusBarWindow.getBarTransitions());
         }
         pw.println("  StatusBarWindowView: ");
         if (mStatusBarWindow != null) {
@@ -3012,8 +3012,8 @@
                     -1;
             if (barMode != -1) {
                 boolean animate = true;
-                if (mStatusBarView != null) {
-                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
+                if (mStatusBarWindow != null && mStatusBarWindow.getBarTransitions() != null) {
+                    mStatusBarWindow.getBarTransitions().transitionTo(barMode, animate);
                 }
                 mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 6789930..0ab2af8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -65,8 +65,6 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.DragDownHelper;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
 import com.android.systemui.tuner.TunerService;
@@ -91,6 +89,7 @@
     private View mBrightnessMirror;
     private LockIcon mLockIcon;
     private PhoneStatusBarView mStatusBarView;
+    private PhoneStatusBarTransitions mBarTransitions;
 
     private int mRightInset = 0;
     private int mLeftInset = 0;
@@ -282,6 +281,12 @@
 
     public void setStatusBarView(PhoneStatusBarView statusBarView) {
         mStatusBarView = statusBarView;
+        mBarTransitions = new PhoneStatusBarTransitions(statusBarView,
+                findViewById(R.id.status_bar_container));
+    }
+
+    public PhoneStatusBarTransitions getBarTransitions() {
+        return mBarTransitions;
     }
 
     public void setService(StatusBar service) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
index a905eba..4cd38fe 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
@@ -17,6 +17,7 @@
 package com.android.systemui.util;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -40,7 +41,7 @@
     private final Sensor mSensor;
     private final AsyncSensorManager mSensorManager;
     private final boolean mUsingBrightnessSensor;
-    private final float mMaxRange;
+    private final float mThreshold;
 
     private SensorEventListener mSensorEventListener = new SensorEventListener() {
         @Override
@@ -59,7 +60,7 @@
     @Inject
     public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
         mSensorManager = sensorManager;
-        Sensor sensor = findBrightnessSensor(context, sensorManager);
+        Sensor sensor = findCustomProxSensor(context, sensorManager);
 
         if (sensor == null) {
             mUsingBrightnessSensor = false;
@@ -69,9 +70,13 @@
         }
         mSensor = sensor;
         if (mSensor != null) {
-            mMaxRange = mSensor.getMaximumRange();
+            if (mUsingBrightnessSensor) {
+                mThreshold = getBrightnessSensorThreshold(context.getResources());
+            } else {
+                mThreshold = mSensor.getMaximumRange();
+            }
         } else {
-            mMaxRange = 0;
+            mThreshold = 0;
         }
     }
 
@@ -79,8 +84,18 @@
         mTag = tag;
     }
 
-    private Sensor findBrightnessSensor(Context context, SensorManager sensorManager) {
-        String sensorType = context.getString(R.string.doze_brightness_sensor_type);
+    /**
+     * Returns a brightness sensor that can be used for proximity purposes.
+     *
+     * @deprecated This method exists for legacy purposes. Use the containing class directly.
+     */
+    @Deprecated
+    public static Sensor findCustomProxSensor(Context context, SensorManager sensorManager) {
+        String sensorType = context.getString(R.string.proximity_sensor_type);
+        if (sensorType.isEmpty()) {
+            return null;
+        }
+
         List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
         Sensor sensor = null;
         for (Sensor s : sensorList) {
@@ -94,6 +109,16 @@
     }
 
     /**
+     * Returns a threshold value that can be used along with {@link #findCustomProxSensor}
+     *
+     * @deprecated This method exists for legacy purposes. Use the containing class directly.
+     */
+    @Deprecated
+    public static float getBrightnessSensorThreshold(Resources resources) {
+        return resources.getFloat(R.dimen.proximity_sensor_threshold);
+    }
+
+    /**
      * Returns {@code false} if a Proximity sensor is not available.
      */
     public boolean getSensorAvailable() {
@@ -141,11 +166,11 @@
     }
 
     private void onSensorEvent(SensorEvent event) {
-        boolean near = event.values[0] < mMaxRange;
         if (mUsingBrightnessSensor) {
-            near = event.values[0] == 0;
+            mNear = event.values[0] <= mThreshold;
+        } else {
+            mNear = event.values[0] < mThreshold;
         }
-        mNear = near;
         mListeners.forEach(proximitySensorListener ->
                 proximitySensorListener.onProximitySensorEvent(
                         new ProximityEvent(mNear, event.timestamp)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 3b5e12c..11b256a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -20,6 +20,8 @@
 import static com.android.systemui.tuner.TunablePadding.FLAG_END;
 import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
@@ -38,6 +40,7 @@
 import android.app.Fragment;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.graphics.drawable.VectorDrawable;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -148,6 +151,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         mScreenDecorations.start();
         // No views added.
@@ -166,6 +171,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         mScreenDecorations.start();
         // Add 2 windows for rounded corners (top and bottom).
@@ -180,6 +187,57 @@
     }
 
     @Test
+    public void testRoundingRadius() {
+        final int testRadius = 1;
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, testRadius);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, testRadius);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, testRadius);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+        mScreenDecorations.start();
+        // Size of corner view should same as rounded_corner_radius{_top|_bottom}
+        assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(testRadius);
+        assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(testRadius);
+        assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(testRadius);
+    }
+
+    @Test
+    public void testRoundingMultipleRadius() {
+        final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+        final int multipleRadiusSize = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 9999);
+        mContext.getOrCreateTestableResources()
+                .addOverride(dimen.rounded_corner_content_padding, 9999);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, true);
+
+        mScreenDecorations.start();
+        // Add 2 windows for rounded corners (top and bottom).
+        verify(mWindowManager, times(2)).addView(any(), any());
+
+        // Add 2 tag listeners for each of the fragments that are needed.
+        verify(mFragmentHostManager, times(2)).addTagListener(any(), any());
+        // One tunable.
+        verify(mTunerService, times(1)).addTunable(any(), any());
+        // One TunablePadding.
+        verify(mTunablePaddingService, times(1)).add(any(), anyString(), anyInt(), anyInt());
+        // Size of corner view should exactly match max(width, height) of R.drawable.rounded
+        assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
+        assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(multipleRadiusSize);
+        assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(multipleRadiusSize);
+    }
+
+    @Test
     public void testCutout() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
@@ -224,6 +282,9 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
         when(mNavigationModeController.addListener(any())).thenReturn(
                 WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
 
@@ -245,6 +306,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
         when(mNavigationModeController.addListener(any())).thenReturn(
                 WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
 
@@ -296,6 +359,8 @@
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         mScreenDecorations.start();
         assertEquals(mScreenDecorations.mRoundedDefault, 20);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1994ccc..81042f4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3151,6 +3151,25 @@
             "subscription_group_uuid_string";
 
     /**
+     * Data switch validation minimal gap time, in milliseconds.
+     *
+     * Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
+     * was recently validated (within this time gap), and Telephony receives a request to switch to
+     * it again, Telephony will skip the validation part and switch to it as soon as connection
+     * is setup, as if it's already validated.
+     *
+     * If the network was validated within the gap but the latest validation result is false, the
+     * validation will not be skipped.
+     *
+     * If not set or set to 0, validation will never be skipped.
+     * The max acceptable value of this config is 24 hours.
+     *
+     * @hide
+     */
+    public static final String KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG =
+            "data_switch_validation_min_gap_LONG";
+
+    /**
     * A boolean property indicating whether this subscription should be managed as an opportunistic
     * subscription.
     *
@@ -3628,6 +3647,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
         sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
+        sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0);
     }
 
     /**