Merge "Attach color space information when attach and queue buffer." into qt-r1-dev
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 3f6880f..650147b 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -177,4 +177,12 @@
      * @param displayId the id of the display on which contents are drawn.
      */
     void onSingleTaskDisplayDrawn(int displayId);
+
+    /**
+     * Called when a task is reparented to a stack on a different display.
+     *
+     * @param taskId id of the task which was moved to a different display.
+     * @param newDisplayId id of the new display.
+     */
+    void onTaskDisplayChanged(int taskId, int newDisplayId);
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 36daf32..b63feb5 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -26,6 +26,7 @@
 /**
  * Classes interested in observing only a subset of changes using ITaskStackListener can extend
  * this class to avoid having to implement all the methods.
+ *
  * @hide
  */
 public abstract class TaskStackListener extends ITaskStackListener.Stub {
@@ -177,4 +178,8 @@
     @Override
     public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
     }
+
+    @Override
+    public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
+    }
 }
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index c45b8ed..3e995b6 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -48,7 +48,8 @@
         return pulseOnNotificationEnabled(user)
                 || pulseOnLongPressEnabled(user)
                 || alwaysOnEnabled(user)
-                || wakeScreenGestureEnabled(user)
+                || wakeLockScreenGestureEnabled(user)
+                || wakeDisplayGestureEnabled(user)
                 || pickupGestureEnabled(user)
                 || tapGestureEnabled(user)
                 || doubleTapGestureEnabled(user);
@@ -105,8 +106,14 @@
     }
 
     /** {@hide} */
-    public boolean wakeScreenGestureEnabled(int user) {
-        return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user)
+    public boolean wakeLockScreenGestureEnabled(int user) {
+        return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
+                && wakeScreenGestureAvailable();
+    }
+
+    /** {@hide} */
+    public boolean wakeDisplayGestureEnabled(int user) {
+        return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, user)
                 && wakeScreenGestureAvailable();
     }
 
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index e8b3ca6..1456ff7 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.media.AudioAttributes;
 import android.os.VibrationEffect;
 
 /** {@hide} */
@@ -23,8 +24,8 @@
 {
     boolean hasVibrator();
     boolean hasAmplitudeControl();
-    void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, String reason,
-            IBinder token);
+    void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes,
+            String reason, IBinder token);
     void cancelVibrate(IBinder token);
 }
 
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 4af514a..a5188e7 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -77,16 +77,12 @@
             return;
         }
         try {
-            mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken);
+            mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
     }
 
-    private static int usageForAttributes(AudioAttributes attributes) {
-        return attributes != null ? attributes.getUsage() : AudioAttributes.USAGE_UNKNOWN;
-    }
-
     @Override
     public void cancel() {
         if (mService == null) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 83e636b..e95e6e0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -88,6 +88,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MemoryIntArray;
+import android.view.Display;
 import android.view.inputmethod.InputMethodSystemProperty;
 
 import com.android.internal.annotations.GuardedBy;
@@ -4073,7 +4074,7 @@
          * preference, this rotation value will be used. Must be one of the
          * {@link android.view.Surface#ROTATION_0 Surface rotation constants}.
          *
-         * @see android.view.Display#getRotation
+         * @see Display#getRotation
          */
         public static final String USER_ROTATION = "user_rotation";
 
@@ -7733,12 +7734,21 @@
         private static final Validator DOZE_TAP_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Gesture that wakes up the display, showing the ambient version of the status bar.
+         * Gesture that wakes up the display, showing some version of the lock screen.
          * @hide
          */
-        public static final String DOZE_WAKE_SCREEN_GESTURE = "doze_wake_screen_gesture";
+        public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_screen_gesture";
 
-        private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+        private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+         * Gesture that wakes up the display, toggling between {@link Display.STATE_OFF} and
+         * {@link Display.STATE_DOZE}.
+         * @hide
+         */
+        public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture";
+
+        private static final Validator DOZE_WAKE_DISPLAY_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
          * Gesture that skips media.
@@ -8964,7 +8974,8 @@
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
             DOZE_TAP_SCREEN_GESTURE,
-            DOZE_WAKE_SCREEN_GESTURE,
+            DOZE_WAKE_LOCK_SCREEN_GESTURE,
+            DOZE_WAKE_DISPLAY_GESTURE,
             NFC_PAYMENT_DEFAULT_COMPONENT,
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
             FACE_UNLOCK_KEYGUARD_ENABLED,
@@ -9136,7 +9147,8 @@
             VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_TAP_SCREEN_GESTURE, DOZE_TAP_SCREEN_GESTURE_VALIDATOR);
-            VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
+            VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
+            VALIDATORS.put(DOZE_WAKE_DISPLAY_GESTURE, DOZE_WAKE_DISPLAY_GESTURE_VALIDATOR);
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
             VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
                     AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index bf0ef94..2c2c295 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1122,6 +1122,9 @@
             if (limit > lineEnd) {
                 limit = lineEnd;
             }
+            if (limit == start) {
+                continue;
+            }
             level[limit - lineStart - 1] =
                     (byte) ((runs[i + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK);
         }
@@ -1220,8 +1223,8 @@
     }
 
     /**
-     * Computes in linear time the results of calling
-     * #getHorizontal for all offsets on a line.
+     * Computes in linear time the results of calling #getHorizontal for all offsets on a line.
+     *
      * @param line The line giving the offsets we compute information for
      * @param clamped Whether to clamp the results to the width of the layout
      * @param primary Whether the results should be the primary or the secondary horizontal
@@ -1257,7 +1260,7 @@
         TextLine.recycle(tl);
 
         if (clamped) {
-            for (int offset = 0; offset <= wid.length; ++offset) {
+            for (int offset = 0; offset < wid.length; ++offset) {
                 if (wid[offset] > mWidth) {
                     wid[offset] = mWidth;
                 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3016fa9..440df89 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1131,7 +1131,13 @@
      */
     public void registerRtFrameCallback(FrameDrawingCallback callback) {
         if (mAttachInfo.mThreadedRenderer != null) {
-            mAttachInfo.mThreadedRenderer.registerRtFrameCallback(callback);
+            mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> {
+                try {
+                    callback.onFrameDraw(frame);
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception while executing onFrameDraw", e);
+                }
+            });
         }
     }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5294714..f9d27bb 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2327,10 +2327,12 @@
 
         private static final int MAX_SUGGESTED_APP_TARGETS = 4;
         private static final int MAX_CHOOSER_TARGETS_PER_APP = 2;
-        private static final int MAX_SHORTCUT_TARGETS_PER_APP = 8;
 
         private static final int MAX_SERVICE_TARGETS = 8;
 
+        private final int mMaxShortcutTargetsPerApp =
+                getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp);
+
         private int mNumShortcutResults = 0;
 
         // Reserve spots for incoming direct share targets by adding placeholders
@@ -2648,7 +2650,7 @@
             final float baseScore = getBaseScore(origTarget, isShortcutResult);
             Collections.sort(targets, mBaseTargetComparator);
 
-            final int maxTargets = isShortcutResult ? MAX_SHORTCUT_TARGETS_PER_APP
+            final int maxTargets = isShortcutResult ? mMaxShortcutTargetsPerApp
                                        : MAX_CHOOSER_TARGETS_PER_APP;
             float lastScore = 0;
             boolean shouldNotify = false;
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 454fe3c..6e97076 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -106,13 +106,6 @@
      */
     public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
 
-    // Flag related to Privacy Indicators
-
-    /**
-     * Whether the Permissions Hub is showing.
-     */
-    public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
-
     // Flags related to Assistant
 
     /**
@@ -152,6 +145,7 @@
      */
     public static final String ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS =
             "assist_handles_shown_frequency_threshold_ms";
+
     // Flag related to clock face
 
     /**
@@ -194,7 +188,12 @@
      * Allow touch passthrough above assist area during a session.
      */
     public static final String ASSIST_TAP_PASSTHROUGH = "assist_tap_passthrough";
-  
+
+    /**
+     * (bool) Whether to show handles when taught.
+     */
+    public static final String ASSIST_HANDLES_SHOW_WHEN_TAUGHT = "assist_handles_show_when_taught";
+
     /**
      * (bool) Whether to use the new BrightLineFalsingManager.
      */
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 56a40a3..da1b72f 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.policy;
 
+import android.content.AutofillOptions;
 import android.content.ContentCaptureOptions;
 import android.content.Context;
 import android.content.res.AssetManager;
@@ -98,6 +99,15 @@
     }
 
     @Override
+    public AutofillOptions getAutofillOptions() {
+        Context activityContext = mActivityContext.get();
+        if (activityContext != null) {
+            return activityContext.getAutofillOptions();
+        }
+        return null;
+    }
+
+    @Override
     public ContentCaptureOptions getContentCaptureOptions() {
         Context activityContext = mActivityContext.get();
         if (activityContext != null) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index afe7954..6f4f337 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -84,6 +84,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
+import android.view.ViewRootImpl;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.Window;
@@ -1150,8 +1151,15 @@
                     navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                     0 /* sideInset */, animate && !disallowAnimate,
                     mForceWindowDrawsBarBackgrounds);
+            boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
             mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
                     && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
+            if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
+                ViewRootImpl vri = getViewRootImpl();
+                if (vri != null) {
+                    vri.requestInvalidateRootRenderNode();
+                }
+            }
 
             boolean statusBarNeedsRightInset = navBarToRightEdge
                     && mNavigationColorViewState.present;
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1bbd87c..1fdb1f3 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -458,7 +458,7 @@
     }
 
     private String getFuncTypeAsString() {
-        if (isRecycled()) throw new IllegalStateException();
+        if (isRecycled()) return "<recycled>";
         if (isConstSupplier()) return "supplier";
         String name = LambdaType.toString(getFlags(MASK_EXPOSED_AS));
         if (name.endsWith("Consumer")) return "consumer";
@@ -466,7 +466,7 @@
         if (name.endsWith("Predicate")) return "predicate";
         if (name.endsWith("Supplier")) return "supplier";
         if (name.endsWith("Runnable")) return "runnable";
-        throw new IllegalStateException("Don't know the string representation of " + name);
+        return name;
     }
 
     /**
@@ -637,6 +637,7 @@
         private static String argCountPrefix(int argCount) {
             switch (argCount) {
                 case MASK_ARG_COUNT: return "";
+                case 0: return "";
                 case 1: return "";
                 case 2: return "Bi";
                 case 3: return "Tri";
@@ -646,7 +647,7 @@
                 case 7: return "Hept";
                 case 8: return "Oct";
                 case 9: return "Nona";
-                default: throw new IllegalArgumentException("" + argCount);
+                default: return "" + argCount + "arg";
             }
         }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4783a25..82c27f0 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1160,6 +1160,7 @@
   /*
    *  Grant the following capabilities to the Bluetooth user:
    *    - CAP_WAKE_ALARM
+   *    - CAP_NET_ADMIN
    *    - CAP_NET_RAW
    *    - CAP_NET_BIND_SERVICE (for DHCP client functionality)
    *    - CAP_SYS_NICE (for setting RT priority for audio-related threads)
@@ -1167,6 +1168,7 @@
 
   if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
     capabilities |= (1LL << CAP_WAKE_ALARM);
+    capabilities |= (1LL << CAP_NET_ADMIN);
     capabilities |= (1LL << CAP_NET_RAW);
     capabilities |= (1LL << CAP_NET_BIND_SERVICE);
     capabilities |= (1LL << CAP_SYS_NICE);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 890ad5e..fef4dcd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -824,6 +824,11 @@
      grants your app this permission. If you don't need this permission, be sure your <a
      href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
      targetSdkVersion}</a> is 4 or higher.
+     <p>Is this permission is not whitelisted for an app that targets an API level before
+     {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
+     <p>Is this permission is not whitelisted for an app that targets an API level
+     {@link android.os.Build.VERSION_CODES#Q} or later the app will be forced into isolated storage.
+     </p>
      -->
     <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:permissionGroup="android.permission-group.UNDEFINED"
@@ -845,6 +850,8 @@
          read/write files in your application-specific directories returned by
          {@link android.content.Context#getExternalFilesDir} and
          {@link android.content.Context#getExternalCacheDir}.
+         <p>Is this permission is not whitelisted for an app that targets an API level before
+         {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
     -->
     <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         android:permissionGroup="android.permission-group.UNDEFINED"
@@ -2537,7 +2544,7 @@
     <!-- @SystemApi @TestApi @hide Allows an application to modify config settings.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WRITE_DEVICE_CONFIG"
-        android:protectionLevel="signature|configurator"/>
+        android:protectionLevel="signature|verifier|configurator"/>
 
     <!-- @SystemApi @hide Allows an application to read config settings.
     <p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8dfb969..77fca8f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -326,6 +326,8 @@
              grantable in its full form to apps that meet special criteria
              per platform policy. Otherwise, a weaker form of the permission
              would be granted. The weak grant depends on the permission.
+             <p>What weak grant means is described in the documentation of
+             the permissions.
         -->
         <flag name="softRestricted" value="0x8" />
         <!-- This permission is restricted immutably which means that its
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1e7f516..8edd284 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4045,11 +4045,38 @@
     </array>
 
     <!-- See DisplayWhiteBalanceController.
-         The ambient color temperature (in cct) to which we fall back when the ambient brightness
-         drops beneath a certain threshold. -->
+         The ambient color temperature (in cct) to which we interpolate towards using the
+         the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnesses
+         and config_displayWhiteBalanceLowLightAmbientBiases. -->
     <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
 
     <!-- See DisplayWhiteBalanceController.
+         A float array containing a list of ambient brightnesses, in Lux. This array,
+         together with config_displayWhiteBalanceHighLightAmbientBiases, is used to generate a
+         lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+         ambient brightness readings to a bias, where the bias is used to linearly interpolate
+         between ambient color temperature and
+         config_displayWhiteBalanceHighLightAmbientColorTemperature.
+         This table is optional. If used, this array must,
+         1) Contain at least two entries
+         2) Be the same length as config_displayWhiteBalanceHighLightAmbientBiases. -->
+    <array name ="config_displayWhiteBalanceHighLightAmbientBrightnesses">
+    </array>
+
+    <!-- See DisplayWhiteBalanceController.
+         An array containing a list of biases. See
+         config_displayWhiteBalanceHighLightAmbientBrightnesses for additional details.
+         This array must be in the range of [0.0, 1.0]. -->
+    <array name ="config_displayWhiteBalanceHighLightAmbientBiases">
+    </array>
+
+    <!-- See DisplayWhiteBalanceController.
+         The ambient color temperature (in cct) to which we interpolate towards using the
+         the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnesses
+         and config_displayWhiteBalanceHighLightAmbientBiases. -->
+    <item name="config_displayWhiteBalanceHighLightAmbientColorTemperature" format="float" type="dimen">8000.0</item>
+
+    <!-- See DisplayWhiteBalanceController.
          A float array containing a list of ambient color temperatures, in Kelvin. This array,
          together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
          lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
@@ -4168,4 +4195,6 @@
          one bar higher than they actually are -->
     <bool name="config_inflateSignalStrength">false</bool>
 
+    <!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q -->
+    <integer name="config_maxShortcutTargetsPerApp">3</integer>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c0a5729..273b143 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2809,6 +2809,7 @@
   <java-symbol type="layout" name="chooser_grid_preview_file" />
   <java-symbol type="id" name="chooser_row_text_option" />
   <java-symbol type="dimen" name="chooser_row_text_option_translate" />
+  <java-symbol type="integer" name="config_maxShortcutTargetsPerApp" />
   <java-symbol type="layout" name="resolve_grid_item" />
   <java-symbol type="id" name="day_picker_view_pager" />
   <java-symbol type="layout" name="day_picker_content_material" />
@@ -3749,6 +3750,9 @@
   <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" />
   <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
+  <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnesses" />
+  <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiases" />
+  <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperature" />
   <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
   <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
   <java-symbol type="drawable" name="ic_action_open" />
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 990161a..93a6b15 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -743,6 +743,9 @@
         assertPrimaryIsTrailingPrevious(
                 RTL + LRI + RTL + LTR + PDI + RTL,
                 new boolean[]{false, false, true, false, false, false, false});
+        assertPrimaryIsTrailingPrevious(
+                "",
+                new boolean[]{false});
     }
 }
 
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 a07bb8f..2874ce6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -346,7 +346,7 @@
 
         CarSystemUIFactory factory = SystemUIFactory.getInstance();
         mCarFacetButtonController = factory.getCarDependencyComponent()
-            .getCarFacetButtonController();
+                .getCarFacetButtonController();
         mNotificationPanelBackground = getDefaultWallpaper();
         mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
 
@@ -925,6 +925,16 @@
                 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
             }
         }
+
+        @Override
+        public void onTaskDisplayChanged(int taskId, int newDisplayId) {
+            try {
+                mCarFacetButtonController.taskChanged(
+                        ActivityTaskManager.getService().getAllStackInfos());
+            } catch (Exception e) {
+                Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+            }
+        }
     }
 
     private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b4912c..17274f4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -644,7 +644,8 @@
         <provider
             android:name="com.android.keyguard.clock.ClockOptionsProvider"
             android:authorities="com.android.keyguard.clock"
-            android:exported="true"
+            android:enabled="false"
+            android:exported="false"
             android:grantUriPermissions="true">
         </provider>
 
diff --git a/packages/SystemUI/res/anim/lock_in.xml b/packages/SystemUI/res/anim/lock_in.xml
deleted file mode 100644
index c7014e8..0000000
--- a/packages/SystemUI/res/anim/lock_in.xml
+++ /dev/null
@@ -1,227 +0,0 @@
-<!-- Copyright (C) 2019 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.
--->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-                 xmlns:aapt="http://schemas.android.com/aapt">
-    <aapt:attr name="android:drawable">
-        <vector android:height="42dp" android:width="32dp" android:viewportHeight="42"
-                android:viewportWidth="32">
-            <group android:name="_R_G">
-                <group android:name="_R_G_L_2_G" android:translateX="1.6669999999999998"
-                       android:translateY="11.992999999999999" android:pivotX="14.333"
-                       android:pivotY="13" android:scaleX="0" android:scaleY="0">
-                    <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#ffffff"
-                          android:strokeLineCap="round" android:strokeLineJoin="round"
-                          android:strokeWidth="2" android:strokeAlpha="1"
-                          android:pathData=" M22.33 21 C22.33,21 6.33,21 6.33,21 C5.6,21 5,20.4 5,19.67 C5,19.67 5,6.33 5,6.33 C5,5.6 5.6,5 6.33,5 C6.33,5 22.33,5 22.33,5 C23.07,5 23.67,5.6 23.67,6.33 C23.67,6.33 23.67,19.67 23.67,19.67 C23.67,20.4 23.07,21 22.33,21c "/>
-                </group>
-                <group android:name="_R_G_L_1_G_N_4_T_0" android:translateX="1.6669999999999998"
-                       android:translateY="11.992999999999999" android:pivotX="14.333"
-                       android:pivotY="13" android:scaleX="0" android:scaleY="0">
-                    <group android:name="_R_G_L_1_G" android:translateX="11.583"
-                           android:translateY="10.257">
-                        <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff"
-                              android:fillAlpha="1" android:fillType="nonZero"
-                              android:pathData=" M2.75 0.25 C4.13,0.25 5.25,1.37 5.25,2.75 C5.25,4.13 4.13,5.25 2.75,5.25 C1.37,5.25 0.25,4.13 0.25,2.75 C0.25,1.37 1.37,0.25 2.75,0.25c "/>
-                    </group>
-                </group>
-                <group android:name="_R_G_L_0_G_N_4_T_0" android:translateX="1.6669999999999998"
-                       android:translateY="11.992999999999999" android:pivotX="14.333"
-                       android:pivotY="13" android:scaleX="0" android:scaleY="0">
-                    <group android:name="_R_G_L_0_G_T_1" android:translateX="14.333"
-                           android:translateY="3.172">
-                        <group android:name="_R_G_L_0_G" android:translateX="-9.667"
-                               android:translateY="-9.667">
-                            <path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#ffffff"
-                                  android:strokeLineCap="round" android:strokeLineJoin="round"
-                                  android:strokeWidth="2" android:strokeAlpha="1"
-                                  android:trimPathStart="0.14" android:trimPathEnd="0.89"
-                                  android:trimPathOffset="0"
-                                  android:pathData=" M14.33 14.33 C14.33,14.33 14.33,9.67 14.33,9.67 C14.33,7.09 12.24,5 9.67,5 C7.09,5 5,7.09 5,9.67 C5,9.67 5,14.33 5,14.33 "/>
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group"/>
-        </vector>
-    </aapt:attr>
-    <target android:name="_R_G_L_2_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="scaleX" android:duration="233"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1.02"
-                                android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.438,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="233"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1.02"
-                                android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.438,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleX" android:duration="117"
-                                android:startOffset="233" android:valueFrom="1.02"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.565,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="117"
-                                android:startOffset="233" android:valueFrom="1.02"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.565,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_4_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="scaleX" android:duration="233"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1.02"
-                                android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.438,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="233"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1.02"
-                                android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.438,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleX" android:duration="117"
-                                android:startOffset="233" android:valueFrom="1.02"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.565,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="117"
-                                android:startOffset="233" android:valueFrom="1.02"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.565,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="trimPathStart" android:duration="50"
-                                android:startOffset="0" android:valueFrom="0.14"
-                                android:valueTo="0.14" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator
-                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="trimPathStart" android:duration="67"
-                                android:startOffset="50" android:valueFrom="0.14"
-                                android:valueTo="0" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator
-                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="trimPathEnd" android:duration="50"
-                                android:startOffset="0" android:valueFrom="0.89"
-                                android:valueTo="0.89" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator
-                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="trimPathEnd" android:duration="67"
-                                android:startOffset="50" android:valueFrom="0.89"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator
-                            android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="translateY" android:duration="150"
-                                android:startOffset="0" android:valueFrom="3.172"
-                                android:valueTo="0.34" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.23,-0.46 0.2,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_N_4_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="scaleX" android:duration="233"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1.02"
-                                android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.438,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="233"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1.02"
-                                android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.438,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleX" android:duration="117"
-                                android:startOffset="233" android:valueFrom="1.02"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.565,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="117"
-                                android:startOffset="233" android:valueFrom="1.02"
-                                android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.565,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="translateX" android:duration="717"
-                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
-                                android:valueType="floatType"/>
-            </set>
-        </aapt:attr>
-    </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/lock_in_circular.xml b/packages/SystemUI/res/anim/lock_in_circular.xml
deleted file mode 100644
index d1e98db..0000000
--- a/packages/SystemUI/res/anim/lock_in_circular.xml
+++ /dev/null
@@ -1,327 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <aapt:attr name="android:drawable" >
-        <vector
-            android:height="32dp"
-            android:viewportHeight="32"
-            android:viewportWidth="32"
-            android:width="32dp" >
-            <group android:name="_R_G" >
-                <group
-                    android:name="_R_G_L_2_G"
-                    android:pivotX="9.583"
-                    android:pivotY="8.916"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="6.416"
-                    android:translateY="10.416999999999998" >
-                    <path
-                        android:name="_R_G_L_2_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#ffffff"
-                        android:fillType="nonZero"
-                        android:pathData=" M17.42 1.75 C17.42,1.75 17.42,14.58 17.42,14.58 C17.42,15.41 16.74,16.08 15.92,16.08 C15.92,16.08 3.25,16.08 3.25,16.08 C2.42,16.08 1.75,15.41 1.75,14.58 C1.75,14.58 1.75,1.75 1.75,1.75 C1.75,1.75 17.42,1.75 17.42,1.75c  M18.92 0.25 C18.92,0.25 0.25,0.25 0.25,0.25 C0.25,0.25 0.25,14.58 0.25,14.58 C0.25,16.24 1.59,17.58 3.25,17.58 C3.25,17.58 15.92,17.58 15.92,17.58 C17.57,17.58 18.92,16.24 18.92,14.58 C18.92,14.58 18.92,0.25 18.92,0.25c " />
-                </group>
-                <group
-                    android:name="_R_G_L_1_G_N_3_T_0"
-                    android:pivotX="9.583"
-                    android:pivotY="8.916"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="6.416"
-                    android:translateY="10.416999999999998" >
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:translateX="7.334"
-                        android:translateY="5.333" >
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="#ffffff"
-                            android:fillType="nonZero"
-                            android:pathData=" M4.25 2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25 C3.35,0.25 4.25,1.15 4.25,2.25c " />
-                        <path
-                            android:name="_R_G_L_1_G_D_1_P_0"
-                            android:pathData=" M2.25 2.25 C2.25,2.25 2.25,5.92 2.25,5.92 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="#ffffff"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="1.5" />
-                    </group>
-                </group>
-                <group
-                    android:name="_R_G_L_0_G_N_3_T_0"
-                    android:pivotX="9.583"
-                    android:pivotY="8.916"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="6.416"
-                    android:translateY="10.416999999999998" >
-                    <group
-                        android:name="_R_G_L_0_G_T_1"
-                        android:translateX="9.583"
-                        android:translateY="-0.861" >
-                        <group
-                            android:name="_R_G_L_0_G"
-                            android:translateX="-8.083"
-                            android:translateY="-8.173" >
-                            <path
-                                android:name="_R_G_L_0_G_D_0_P_0"
-                                android:pathData=" M12.42 12.6 C12.42,12.6 12.42,8.17 12.42,8.17 C12.42,5.83 10.48,3.75 8.08,3.75 C5.69,3.75 3.75,5.83 3.75,8.17 C3.75,8.17 3.75,12.6 3.75,12.6 "
-                                android:strokeAlpha="1"
-                                android:strokeColor="#ffffff"
-                                android:strokeLineCap="round"
-                                android:strokeLineJoin="round"
-                                android:strokeWidth="1.5"
-                                android:trimPathEnd="0.9"
-                                android:trimPathOffset="0"
-                                android:trimPathStart="0.15" />
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-
-    <target android:name="_R_G_L_2_G" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_3_T_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0.15"
-                    android:valueTo="0.15"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="50"
-                    android:valueFrom="0.15"
-                    android:valueTo="0"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0.9"
-                    android:valueTo="0.9"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="50"
-                    android:valueFrom="0.9"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_T_1" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="150"
-                    android:pathData="M 9.583,-0.861C 9.583,-1.32784640645981 9.583,-3.19515359354019 9.583,-3.662"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="0" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_N_3_T_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="717"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/lock_in_filled.xml b/packages/SystemUI/res/anim/lock_in_filled.xml
deleted file mode 100644
index 4cde38d..0000000
--- a/packages/SystemUI/res/anim/lock_in_filled.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <aapt:attr name="android:drawable" >
-        <vector
-            android:height="32dp"
-            android:viewportHeight="32"
-            android:viewportWidth="32"
-            android:width="32dp" >
-            <group android:name="_R_G" >
-                <group
-                    android:name="_R_G_L_1_G"
-                    android:pivotX="10.917"
-                    android:pivotY="9.583"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="5.083"
-                    android:translateY="10.417" >
-                    <path
-                        android:name="_R_G_L_1_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#ffffff"
-                        android:fillType="nonZero"
-                        android:pathData=" M18.92 0.25 C18.92,0.25 2.92,0.25 2.92,0.25 C1.45,0.25 0.25,1.45 0.25,2.92 C0.25,2.92 0.25,16.25 0.25,16.25 C0.25,17.72 1.45,18.92 2.92,18.92 C2.92,18.92 18.92,18.92 18.92,18.92 C20.38,18.92 21.58,17.72 21.58,16.25 C21.58,16.25 21.58,2.92 21.58,2.92 C21.58,1.45 20.38,0.25 18.92,0.25c  M10.92 12.25 C9.45,12.25 8.25,11.05 8.25,9.58 C8.25,8.12 9.45,6.92 10.92,6.92 C12.38,6.92 13.58,8.12 13.58,9.58 C13.58,11.05 12.38,12.25 10.92,12.25c " />
-                </group>
-                <group
-                    android:name="_R_G_L_0_G_N_2_T_0"
-                    android:pivotX="10.917"
-                    android:pivotY="9.583"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="5.083"
-                    android:translateY="10.417" >
-                    <group
-                        android:name="_R_G_L_0_G_T_1"
-                        android:translateX="10.917"
-                        android:translateY="0.579" >
-                        <group
-                            android:name="_R_G_L_0_G"
-                            android:translateX="-9"
-                            android:translateY="-9" >
-                            <path
-                                android:name="_R_G_L_0_G_D_0_P_0"
-                                android:pathData=" M13 13 C13,13 13,9 13,9 C13,6.79 11.21,5 9,5 C6.79,5 5,6.79 5,9 C5,9 5,13 5,13 "
-                                android:strokeAlpha="1"
-                                android:strokeColor="#ffffff"
-                                android:strokeLineCap="round"
-                                android:strokeLineJoin="round"
-                                android:strokeWidth="2" />
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-
-    <target android:name="_R_G_L_1_G" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_T_1" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="150"
-                    android:pathData="M 10.917,0.579C 10.917,-0.14248990631104008 10.917,-3.02851009368896 10.917,-3.75"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="0" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_N_2_T_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="717"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/lock_in_rounded.xml b/packages/SystemUI/res/anim/lock_in_rounded.xml
deleted file mode 100644
index 7c8cf9d..0000000
--- a/packages/SystemUI/res/anim/lock_in_rounded.xml
+++ /dev/null
@@ -1,336 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <aapt:attr name="android:drawable" >
-        <vector
-            android:height="38dp"
-            android:viewportHeight="38"
-            android:viewportWidth="32"
-            android:width="32dp" >
-            <group android:name="_R_G" >
-                <group
-                    android:name="_R_G_L_2_G"
-                    android:pivotX="14.667"
-                    android:pivotY="12.667"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="1.3330000000000002"
-                    android:translateY="10.333" >
-                    <path
-                        android:name="_R_G_L_2_G_D_0_P_0"
-                        android:pathData=" M22.09 5 C22.09,5 6,5 6,5 C5.45,5 5,5.45 5,6 C5,6 5,19.33 5,19.33 C5,19.89 5.45,20.33 6,20.33 C6,20.33 23.33,20.33 23.33,20.33 C23.89,20.33 24.33,19.89 24.33,19.33 C24.33,19.33 24.33,6 24.33,6 C24.33,5.45 23.89,5 23.33,5 C23.33,5 22.09,5 22.09,5c "
-                        android:strokeAlpha="1"
-                        android:strokeColor="#ffffff"
-                        android:strokeLineCap="round"
-                        android:strokeLineJoin="round"
-                        android:strokeWidth="1.5" />
-                </group>
-                <group
-                    android:name="_R_G_L_1_G_N_3_T_0"
-                    android:pivotX="14.667"
-                    android:pivotY="12.667"
-                    android:scaleX="0"
-                    android:scaleY="0"
-                    android:translateX="1.3330000000000002"
-                    android:translateY="10.333" >
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:translateX="12.416"
-                        android:translateY="10.417" >
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:fillAlpha="1"
-                            android:fillColor="#ffffff"
-                            android:fillType="nonZero"
-                            android:pathData=" M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c " />
-                    </group>
-                </group>
-                <group android:name="_R_G_L_0_G_N_3_T_0_M" >
-                    <group
-                        android:name="_R_G_L_0_G_N_3_T_0"
-                        android:pivotX="14.667"
-                        android:pivotY="12.667"
-                        android:scaleX="0"
-                        android:scaleY="0"
-                        android:translateX="1.3330000000000002"
-                        android:translateY="10.333" >
-                        <group
-                            android:name="_R_G_L_0_G_T_1"
-                            android:translateX="14.666"
-                            android:translateY="3.769" >
-                            <group
-                                android:name="_R_G_L_0_G"
-                                android:translateX="-9.333"
-                                android:translateY="-9.713" >
-                                <path
-                                    android:name="_R_G_L_0_G_D_0_P_0"
-                                    android:pathData=" M13.67 13.8 C13.67,13.8 13.67,8.94 13.67,8.94 C13.63,6.75 11.69,5 9.33,5.04 C6.98,5 5.04,6.75 5,8.94 C5,8.94 5,13.8 5,13.8 "
-                                    android:strokeAlpha="1"
-                                    android:strokeColor="#ffffff"
-                                    android:strokeLineCap="round"
-                                    android:strokeLineJoin="round"
-                                    android:strokeWidth="1.5"
-                                    android:trimPathEnd="0.9"
-                                    android:trimPathOffset="0"
-                                    android:trimPathStart="0.14" />
-                            </group>
-                        </group>
-                    </group>
-                </group>
-            </group>
-            <group android:name="time_group" />
-        </vector>
-    </aapt:attr>
-
-    <target android:name="_R_G_L_2_G" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_1_G_N_3_T_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0.14"
-                    android:valueTo="0.14"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="50"
-                    android:valueFrom="0.14"
-                    android:valueTo="0"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="50"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0.9"
-                    android:valueTo="0.9"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="50"
-                    android:valueFrom="0.9"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_T_1" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="150"
-                    android:pathData="M 14.666,3.769C 14.666,3.18868762874603 14.666,0.8673123712539699 14.666,0.287"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="0" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_N_3_T_0" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="233"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1.025"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.277 0.44,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleX"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="117"
-                    android:propertyName="scaleY"
-                    android:startOffset="233"
-                    android:valueFrom="1.025"
-                    android:valueTo="1"
-                    android:valueType="floatType" >
-                    <aapt:attr name="android:interpolator" >
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_N_3_T_0_M" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="0"
-                    android:propertyName="scaleX"
-                    android:startOffset="50"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="time_group" >
-        <aapt:attr name="android:animation" >
-            <set android:ordering="together" >
-                <objectAnimator
-                    android:duration="717"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
-            </set>
-        </aapt:attr>
-    </target>
-
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
deleted file mode 100644
index b7b21fa..0000000
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#242424" /> <!-- 14% of white -->
-    <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
-        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
-    <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
deleted file mode 100644
index dce9ce1..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 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.
--->
-
-
-<com.android.systemui.privacy.OngoingPrivacyChip
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/privacy_chip"
-    android:layout_height="match_parent"
-    android:layout_width="wrap_content"
-    android:layout_gravity="center_vertical|end"
-    android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:focusable="true" >
-
-        <FrameLayout
-            android:id="@+id/background"
-            android:layout_height="@dimen/ongoing_appops_chip_height"
-            android:minWidth="48dp"
-            android:layout_width="wrap_content" >
-                <LinearLayout
-                    android:id="@+id/icons_container"
-                    android:layout_height="match_parent"
-                    android:layout_width="wrap_content"
-                    android:gravity="center_vertical"
-                    />
-          </FrameLayout>
-</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
deleted file mode 100644
index 5595b13..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 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.
--->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:textDirection="locale"
-    android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
-/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index cd9f780..54fb216 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -22,19 +22,11 @@
     android:layout_height="@*android:dimen/quick_qs_offset_height"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:gravity="center"
     android:orientation="horizontal"
     android:clickable="true"
     android:paddingStart="@dimen/status_bar_padding_start"
     android:paddingEnd="@dimen/status_bar_padding_end" >
 
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:gravity="center_vertical|start" >
-
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
         android:layout_width="wrap_content"
@@ -46,23 +38,4 @@
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         systemui:showDark="false" />
-    </LinearLayout>
-
-    <android.widget.Space
-        android:id="@+id/space"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="center_vertical|center_horizontal"
-        android:visibility="gone" />
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:gravity="center_vertical|end" >
-
-    <include layout="@layout/ongoing_privacy_chip" />
-
-    </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e97055f0..2e2666e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -94,6 +94,10 @@
     <item type="id" name="top_roundess_animator_start_tag"/>
     <item type="id" name="top_roundess_animator_end_tag"/>
 
+    <item type="id" name="keyguard_hun_animator_tag"/>
+    <item type="id" name="keyguard_hun_animator_start_tag"/>
+    <item type="id" name="keyguard_hun_animator_end_tag"/>
+
     <!-- Accessibility actions for the notification menu -->
     <item type="id" name="action_snooze_undo"/>
     <item type="id" name="action_snooze_shorter"/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index bd2b19c..c215d0f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -87,6 +87,14 @@
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
 
     /**
+     * Called when a task is reparented to a stack on a different display.
+     *
+     * @param taskId id of the task which was moved to a different display.
+     * @param newDisplayId id of the new display.
+     */
+    public void onTaskDisplayChanged(int taskId, int newDisplayId) { }
+
+    /**
      * Checks that the current user matches the process. Since
      * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
      * {@link TaskStackChangeListener} should make this call to verify that we don't act on events
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index c89f2ab..d570a58 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -112,7 +112,7 @@
 
     @Override
     public void onPinnedActivityRestartAttempt(boolean clearedTask)
-            throws RemoteException{
+            throws RemoteException {
         mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
         mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
                 .sendToTarget();
@@ -154,7 +154,7 @@
     public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
             int requestedDisplayId) throws RemoteException {
         mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
-                 requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
+                requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
     }
 
     @Override
@@ -208,6 +208,11 @@
                 0 /* unused */).sendToTarget();
     }
 
+    @Override
+    public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
+    }
+
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -228,6 +233,7 @@
         private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
         private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
         private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
+        private static final int ON_TASK_DISPLAY_CHANGED = 20;
 
 
         public H(Looper looper) {
@@ -313,7 +319,7 @@
                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i)
-                                .onActivityLaunchOnSecondaryDisplayRerouted(info);
+                                    .onActivityLaunchOnSecondaryDisplayRerouted(info);
                         }
                         break;
                     }
@@ -370,6 +376,12 @@
                         }
                         break;
                     }
+                    case ON_TASK_DISPLAY_CHANGED: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
+                        }
+                        break;
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 0ee9bff..6c36bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -48,7 +48,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.PowerUI;
-import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -285,7 +284,6 @@
     @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
     @Inject Lazy<AutoHideController> mAutoHideController;
     @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
-    @Inject Lazy<PrivacyItemController> mPrivacyItemController;
     @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
     @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
     @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
@@ -469,7 +467,6 @@
         mProviders.put(ForegroundServiceNotificationListener.class,
                 mForegroundServiceNotificationListener::get);
         mProviders.put(ClockManager.class, mClockManager::get);
-        mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
         mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
         mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
         mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index e9eda4f..9a0e9fc 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.HandlerThread;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.util.Size;
@@ -45,12 +46,27 @@
     // We delayed destroy render context that subsequent render requests have chance to cancel it.
     // This is to avoid destroying then recreating render context in a very short time.
     private static final int DELAY_FINISH_RENDERING = 1000;
+    private HandlerThread mWorker;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWorker = new HandlerThread(TAG);
+        mWorker.start();
+    }
 
     @Override
     public Engine onCreateEngine() {
         return new GLEngine(this);
     }
 
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mWorker.quitSafely();
+        mWorker = null;
+    }
+
     class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
         // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
         // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
@@ -98,13 +114,14 @@
         @Override
         public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
                 float yOffsetStep, int xPixelOffset, int yPixelOffset) {
-            mRenderer.updateOffsets(xOffset, yOffset);
+            mWorker.getThreadHandler().post(() -> mRenderer.updateOffsets(xOffset, yOffset));
         }
 
         @Override
         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
-            mRenderer.updateAmbientMode(inAmbientMode,
-                    (mNeedTransition || animationDuration != 0) ? animationDuration : 0);
+            long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0;
+            mWorker.getThreadHandler().post(
+                    () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
         }
 
         @Override
@@ -113,53 +130,61 @@
                 mController.removeCallback(this /* StateListener */);
             }
             mController = null;
-            mRenderer.finish();
-            mRenderer = null;
-            mEglHelper.finish();
-            mEglHelper = null;
-            getSurfaceHolder().getSurface().hwuiDestroy();
+
+            mWorker.getThreadHandler().post(() -> {
+                mRenderer.finish();
+                mRenderer = null;
+                mEglHelper.finish();
+                mEglHelper = null;
+                getSurfaceHolder().getSurface().hwuiDestroy();
+            });
         }
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
-            mEglHelper.init(holder);
-            mRenderer.onSurfaceCreated();
+            mWorker.getThreadHandler().post(() -> {
+                mEglHelper.init(holder);
+                mRenderer.onSurfaceCreated();
+            });
         }
 
         @Override
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-            mRenderer.onSurfaceChanged(width, height);
-            mNeedRedraw = true;
+            mWorker.getThreadHandler().post(() -> {
+                mRenderer.onSurfaceChanged(width, height);
+                mNeedRedraw = true;
+            });
         }
 
         @Override
         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
-            if (mNeedRedraw) {
-                preRender();
-                requestRender();
-                postRender();
-                mNeedRedraw = false;
-            }
-        }
-
-        @Override
-        public SurfaceHolder getHolder() {
-            return getSurfaceHolder();
+            mWorker.getThreadHandler().post(() -> {
+                if (mNeedRedraw) {
+                    preRender();
+                    requestRender();
+                    postRender();
+                    mNeedRedraw = false;
+                }
+            });
         }
 
         @Override
         public void onStatePostChange() {
             // When back to home, we try to release EGL, which is preserved in lock screen or aod.
             if (mController.getState() == StatusBarState.SHADE) {
-                scheduleFinishRendering();
+                mWorker.getThreadHandler().post(this::scheduleFinishRendering);
             }
         }
 
         @Override
         public void preRender() {
+            mWorker.getThreadHandler().post(this::preRenderInternal);
+        }
+
+        private void preRenderInternal() {
             boolean contextRecreated = false;
             Rect frame = getSurfaceHolder().getSurfaceFrame();
-            getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
+            cancelFinishRenderingTask();
 
             // Check if we need to recreate egl context.
             if (!mEglHelper.hasEglContext()) {
@@ -187,6 +212,10 @@
 
         @Override
         public void requestRender() {
+            mWorker.getThreadHandler().post(this::requestRenderInternal);
+        }
+
+        private void requestRenderInternal() {
             Rect frame = getSurfaceHolder().getSurfaceFrame();
             boolean readyToRender = mEglHelper.hasEglContext() && mEglHelper.hasEglSurface()
                     && frame.width() > 0 && frame.height() > 0;
@@ -205,12 +234,16 @@
 
         @Override
         public void postRender() {
-            scheduleFinishRendering();
+            mWorker.getThreadHandler().post(this::scheduleFinishRendering);
+        }
+
+        private void cancelFinishRenderingTask() {
+            mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
         }
 
         private void scheduleFinishRendering() {
-            getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
-            getMainThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
+            cancelFinishRenderingTask();
+            mWorker.getThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
         }
 
         private void finishRendering() {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 858ed6d..7a74dba 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -20,7 +20,6 @@
 
 import android.app.AppOpsManager;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -62,7 +61,6 @@
     private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
-    private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
 
     @GuardedBy("mActiveItems")
@@ -80,14 +78,8 @@
 
     @Inject
     public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
-        this(context, bgLooper, new PermissionFlagsCache(context));
-    }
-
-    @VisibleForTesting
-    protected AppOpsControllerImpl(Context context, Looper bgLooper, PermissionFlagsCache cache) {
         mContext = context;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mFlagsCache = cache;
         mBGHandler = new H(bgLooper);
         final int numOps = OPS.length;
         for (int i = 0; i < numOps; i++) {
@@ -220,59 +212,6 @@
     }
 
     /**
-     * Does the app-op code refer to a user sensitive permission for the specified user id
-     * and package. Only user sensitive permission should be shown to the user by default.
-     *
-     * @param appOpCode The code of the app-op.
-     * @param uid The uid of the user.
-     * @param packageName The name of the package.
-     *
-     * @return {@code true} iff the app-op item is user sensitive
-     */
-    private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
-        String permission = AppOpsManager.opToPermission(appOpCode);
-        if (permission == null) {
-            return false;
-        }
-        int permFlags = mFlagsCache.getPermissionFlags(permission,
-                packageName, UserHandle.getUserHandleForUid(uid));
-        return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
-    }
-
-    /**
-     * Does the app-op item refer to an operation that should be shown to the user.
-     * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
-     * permission should be shown to the user by default.
-     *
-     * @param item The item
-     *
-     * @return {@code true} iff the app-op item should be shown to the user
-     */
-    private boolean isUserVisible(AppOpItem item) {
-        return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
-    }
-
-
-    /**
-     * Does the app-op, uid and package name, refer to an operation that should be shown to the
-     * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
-     * ops that refer to user sensitive permission should be shown to the user by default.
-     *
-     * @param item The item
-     *
-     * @return {@code true} iff the app-op for should be shown to the user
-     */
-    private boolean isUserVisible(int appOpCode, int uid, String packageName) {
-        // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
-        // which may be user senstive, so for now always show it to the user.
-        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
-            return true;
-        }
-
-        return isUserSensitive(appOpCode, uid, packageName);
-    }
-
-    /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks.
      *
      * @return List of active AppOps information
@@ -295,8 +234,8 @@
             final int numActiveItems = mActiveItems.size();
             for (int i = 0; i < numActiveItems; i++) {
                 AppOpItem item = mActiveItems.get(i);
-                if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item)) {
+                if ((userId == UserHandle.USER_ALL
+                        || UserHandle.getUserId(item.getUid()) == userId)) {
                     list.add(item);
                 }
             }
@@ -305,8 +244,8 @@
             final int numNotedItems = mNotedItems.size();
             for (int i = 0; i < numNotedItems; i++) {
                 AppOpItem item = mNotedItems.get(i);
-                if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item)) {
+                if ((userId == UserHandle.USER_ALL
+                        || UserHandle.getUserId(item.getUid()) == userId)) {
                     list.add(item);
                 }
             }
@@ -317,7 +256,7 @@
     @Override
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
         if (updateActives(code, uid, packageName, active)) {
-            mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active));
+            notifySuscribers(code, uid, packageName, active);
         }
     }
 
@@ -328,12 +267,11 @@
         }
         if (result != AppOpsManager.MODE_ALLOWED) return;
         addNoted(code, uid, packageName);
-        mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true));
+        notifySuscribers(code, uid, packageName, true);
     }
 
     private void notifySuscribers(int code, int uid, String packageName, boolean active) {
-        if (mCallbacksByCode.containsKey(code)
-                && isUserVisible(code, uid, packageName)) {
+        if (mCallbacksByCode.containsKey(code)) {
             for (Callback cb: mCallbacksByCode.get(code)) {
                 cb.onActiveStateChanged(code, uid, packageName, active);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
deleted file mode 100644
index f02c7af..0000000
--- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 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.appops
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.util.ArrayMap
-import com.android.internal.annotations.VisibleForTesting
-
-private data class PermissionFlag(val flag: Int, val timestamp: Long)
-
-private data class PermissionFlagKey(
-    val permission: String,
-    val packageName: String,
-    val user: UserHandle
-)
-
-internal const val CACHE_EXPIRATION = 10000L
-
-/**
- * Cache for PackageManager's PermissionFlags.
- *
- * Flags older than [CACHE_EXPIRATION] will be retrieved again.
- */
-internal open class PermissionFlagsCache(context: Context) {
-    private val packageManager = context.packageManager
-    private val permissionFlagsCache = ArrayMap<PermissionFlagKey, PermissionFlag>()
-
-    /**
-     * Retrieve permission flags from cache or PackageManager. There parameters will be passed
-     * directly to [PackageManager].
-     *
-     * Calls to this method should be done from a background thread.
-     */
-    fun getPermissionFlags(permission: String, packageName: String, user: UserHandle): Int {
-        val key = PermissionFlagKey(permission, packageName, user)
-        val now = getCurrentTime()
-        val value = permissionFlagsCache.getOrPut(key) {
-            PermissionFlag(getFlags(key), now)
-        }
-        if (now - value.timestamp > CACHE_EXPIRATION) {
-            val newValue = PermissionFlag(getFlags(key), now)
-            permissionFlagsCache.put(key, newValue)
-            return newValue.flag
-        } else {
-            return value.flag
-        }
-    }
-
-    private fun getFlags(key: PermissionFlagKey) =
-            packageManager.getPermissionFlags(key.permission, key.packageName, key.user)
-
-    @VisibleForTesting
-    protected open fun getCurrentTime() = System.currentTimeMillis()
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 0af333e..f9ddeae 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -67,6 +67,7 @@
     private static final boolean DEFAULT_SUPPRESS_ON_LOCKSCREEN = false;
     private static final boolean DEFAULT_SUPPRESS_ON_LAUNCHER = false;
     private static final boolean DEFAULT_SUPPRESS_ON_APPS = true;
+    private static final boolean DEFAULT_SHOW_WHEN_TAUGHT = false;
 
     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
@@ -309,7 +310,7 @@
             return;
         }
 
-        if (mIsDozing || mIsNavBarHidden || mOnLockscreen) {
+        if (mIsDozing || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) {
             mAssistHandleCallbacks.hide();
         } else if (justUnlocked) {
             long currentEpochDay = LocalDate.now().toEpochDay();
@@ -429,6 +430,12 @@
                 DEFAULT_SUPPRESS_ON_APPS);
     }
 
+    private boolean getShowWhenTaught() {
+        return mPhenotypeHelper.getBoolean(
+                SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT,
+                DEFAULT_SHOW_WHEN_TAUGHT);
+    }
+
     @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "Current AssistHandleReminderExpBehavior State:");
@@ -480,5 +487,9 @@
                 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS
                 + "="
                 + getSuppressOnApps());
+        pw.println(prefix + "      "
+                + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT
+                + "="
+                + getShowWhenTaught());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java b/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java
index 8eea368..65a9fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/PerimeterPathGuide.java
@@ -287,9 +287,12 @@
         float accum = 0;
         for (int i = 0; i < mRegions.length; i++) {
             mRegions[i].normalizedLength = mRegions[i].absoluteLength / perimeterLength;
-            accum += mRegions[i].normalizedLength;
-            mRegions[i].endCoordinate = accum;
+            accum += mRegions[i].absoluteLength;
+            mRegions[i].endCoordinate = accum / perimeterLength;
         }
+        // Ensure that the last coordinate is 1. Setting it explicitly to avoid floating point
+        // error.
+        mRegions[mRegions.length - 1].endCoordinate = 1f;
     }
 
     private CircularCornerPathRenderer.Corner getRotatedCorner(
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 1521889..3cc8ec9a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -96,7 +96,7 @@
             mInternalFalsingManager = new FalsingManagerImpl(context);
         } else {
             mInternalFalsingManager = new BrightLineFalsingManager(
-                    new FalsingDataProvider(context),
+                    new FalsingDataProvider(context.getResources().getDisplayMetrics()),
                     Dependency.get(AsyncSensorManager.class)
             );
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 4975e63..8b11ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.classifier.brightline;
 
-import android.content.Context;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
@@ -51,8 +50,7 @@
     private MotionEvent mFirstRecentMotionEvent;
     private MotionEvent mLastMotionEvent;
 
-    public FalsingDataProvider(Context context) {
-        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+    public FalsingDataProvider(DisplayMetrics displayMetrics) {
         mXdpi = displayMetrics.xdpi;
         mYdpi = displayMetrics.ydpi;
         mWidthPixels = displayMetrics.widthPixels;
@@ -145,12 +143,20 @@
 
     boolean isHorizontal() {
         recalculateData();
+        if (mRecentMotionEvents.isEmpty()) {
+            return false;
+        }
+
         return Math.abs(mFirstRecentMotionEvent.getX() - mLastMotionEvent.getX()) > Math
                 .abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY());
     }
 
     boolean isRight() {
         recalculateData();
+        if (mRecentMotionEvents.isEmpty()) {
+            return false;
+        }
+
         return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX();
     }
 
@@ -160,6 +166,10 @@
 
     boolean isUp() {
         recalculateData();
+        if (mRecentMotionEvents.isEmpty()) {
+            return false;
+        }
+
         return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
     }
 
@@ -168,8 +178,13 @@
             return;
         }
 
-        mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
-        mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+        if (mRecentMotionEvents.isEmpty()) {
+            mFirstRecentMotionEvent = null;
+            mLastMotionEvent = null;
+        } else {
+            mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
+            mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+        }
 
         calculateAngleInternal();
 
@@ -245,5 +260,7 @@
         }
 
         mRecentMotionEvents.clear();
+
+        mDirty = true;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index cdcf660..66b4ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -127,15 +127,15 @@
                         true /* touchscreen */),
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
-                        Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
+                        Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                         mConfig.wakeScreenGestureAvailable() && alwaysOn,
                         DozeLog.REASON_SENSOR_WAKE_UP,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
-                        Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
-                        mConfig.wakeScreenGestureAvailable() && alwaysOn,
+                        Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+                        mConfig.wakeScreenGestureAvailable(),
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
                         false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index 1744c4e..d7411260 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -60,6 +60,9 @@
  */
 public class EglHelper {
     private static final String TAG = EglHelper.class.getSimpleName();
+    // Below two constants make drawing at low priority, so other things can preempt our drawing.
+    private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
+    private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
 
     private EGLDisplay mEglDisplay;
     private EGLConfig mEglConfig;
@@ -181,7 +184,8 @@
      * @return true if EglContext is ready.
      */
     public boolean createEglContext() {
-        int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+        int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
+                EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
         mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
         if (mEglContext == EGL_NO_CONTEXT) {
             Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
index b615a5f..60ea1cd 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
@@ -17,7 +17,6 @@
 package com.android.systemui.glwallpaper;
 
 import android.util.Size;
-import android.view.SurfaceHolder;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -83,12 +82,6 @@
     interface SurfaceProxy {
 
         /**
-         * Get surface holder.
-         * @return surface holder.
-         */
-        SurfaceHolder getHolder();
-
-        /**
          * Ask proxy to start rendering frame to surface.
          */
         void requestRender();
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
deleted file mode 100644
index a5a915b..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 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.privacy
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.Gravity
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.LinearLayout
-import com.android.systemui.R
-
-class OngoingPrivacyChip @JvmOverloads constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttrs: Int = 0,
-    defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
-
-    private val iconMarginExpanded = context.resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_chip_icon_margin_expanded)
-    private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_chip_icon_margin_collapsed)
-    private val iconSize =
-            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
-    private val iconColor = context.resources.getColor(
-            R.color.status_bar_clock_color, context.theme)
-    private val sidePadding =
-            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
-    private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
-    private lateinit var iconsContainer: LinearLayout
-    private lateinit var back: FrameLayout
-    var expanded = false
-        set(value) {
-            if (value != field) {
-                field = value
-                updateView()
-            }
-        }
-
-    var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
-    var privacyList = emptyList<PrivacyItem>()
-        set(value) {
-            field = value
-            builder = PrivacyDialogBuilder(context, value)
-            updateView()
-        }
-
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-
-        back = findViewById(R.id.background)
-        iconsContainer = findViewById(R.id.icons_container)
-    }
-
-    // Should only be called if the builder icons or app changed
-    private fun updateView() {
-        back.background = if (expanded) backgroundDrawable else null
-        val padding = if (expanded) sidePadding else 0
-        back.setPaddingRelative(padding, 0, padding, 0)
-        fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
-            iconsContainer.removeAllViews()
-            dialogBuilder.generateIcons().forEachIndexed { i, it ->
-                it.mutate()
-                it.setTint(iconColor)
-                val image = ImageView(context).apply {
-                    setImageDrawable(it)
-                    scaleType = ImageView.ScaleType.CENTER_INSIDE
-                }
-                iconsContainer.addView(image, iconSize, iconSize)
-                if (i != 0) {
-                    val lp = image.layoutParams as MarginLayoutParams
-                    lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
-                    image.layoutParams = lp
-                }
-            }
-        }
-
-        if (!privacyList.isEmpty()) {
-            generateContentDescription()
-            setIcons(builder, iconsContainer)
-            val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
-            lp.gravity = Gravity.CENTER_VERTICAL or
-                    (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
-            iconsContainer.layoutParams = lp
-        } else {
-            iconsContainer.removeAllViews()
-        }
-        requestLayout()
-    }
-
-    private fun generateContentDescription() {
-        val typesText = builder.joinTypes()
-        contentDescription = context.getString(
-                R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
deleted file mode 100644
index d08a373..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 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.privacy
-
-import android.content.Context
-import android.graphics.drawable.Drawable
-import com.android.systemui.R
-
-class PrivacyDialogBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
-
-    val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
-    val types: List<PrivacyType>
-    private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
-    private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
-
-    init {
-        appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
-                .toList()
-                .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
-                        { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
-        types = itemsList.map { it.privacyType }.distinct().sorted()
-    }
-
-    fun generateIconsForApp(types: List<PrivacyType>): List<Drawable> {
-        return types.sorted().map { it.getIcon(context) }
-    }
-
-    fun generateIcons() = types.map { it.getIcon(context) }
-
-    private fun <T> List<T>.joinWithAnd(): StringBuilder {
-        return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
-            append(lastSeparator)
-            append(this@joinWithAnd.last())
-        }
-    }
-
-    fun joinTypes(): String {
-        return when (types.size) {
-            0 -> ""
-            1 -> types[0].getName(context)
-            else -> types.map { it.getName(context) }.joinWithAnd().toString()
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
deleted file mode 100644
index 2909424..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 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.privacy
-
-import android.content.Context
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.os.UserHandle
-import android.util.IconDrawableFactory
-import com.android.systemui.R
-
-typealias Privacy = PrivacyType
-
-enum class PrivacyType(private val nameId: Int, val iconId: Int) {
-    // This is uses the icons used by the corresponding permission groups in the AndroidManifest
-    TYPE_CAMERA(R.string.privacy_type_camera,
-            com.android.internal.R.drawable.perm_group_camera),
-    TYPE_MICROPHONE(R.string.privacy_type_microphone,
-            com.android.internal.R.drawable.perm_group_microphone),
-    TYPE_LOCATION(R.string.privacy_type_location,
-            com.android.internal.R.drawable.perm_group_location);
-
-    fun getName(context: Context) = context.resources.getString(nameId)
-
-    fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
-}
-
-data class PrivacyItem(
-    val privacyType: PrivacyType,
-    val application: PrivacyApplication
-)
-
-data class PrivacyApplication(val packageName: String, val uid: Int, val context: Context)
-    : Comparable<PrivacyApplication> {
-
-    override fun compareTo(other: PrivacyApplication): Int {
-        return applicationName.compareTo(other.applicationName)
-    }
-
-    private val applicationInfo: ApplicationInfo? by lazy {
-        try {
-            val userHandle = UserHandle.getUserHandleForUid(uid)
-            context.createPackageContextAsUser(packageName, 0, userHandle).getPackageManager()
-                    .getApplicationInfo(packageName, 0)
-        } catch (_: PackageManager.NameNotFoundException) {
-            null
-        }
-    }
-    val icon: Drawable by lazy {
-        applicationInfo?.let {
-            try {
-                val iconFactory = IconDrawableFactory.newInstance(context, true)
-                iconFactory.getBadgedIcon(it, UserHandle.getUserId(uid))
-            } catch (_: Exception) {
-                null
-            }
-        } ?: context.getDrawable(android.R.drawable.sym_def_app_icon)
-    }
-
-    val applicationName: String by lazy {
-        applicationInfo?.let {
-            context.packageManager.getApplicationLabel(it) as String
-        } ?: packageName
-    }
-
-    override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
deleted file mode 100644
index 82a2c1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2018 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.privacy
-
-import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import android.os.UserHandle
-import android.os.UserManager
-import android.provider.DeviceConfig
-import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dependency.BG_HANDLER_NAME
-import com.android.systemui.Dependency.MAIN_HANDLER_NAME
-import com.android.systemui.R
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
-import com.android.systemui.Dumpable
-import java.io.FileDescriptor
-import java.io.PrintWriter
-import java.lang.ref.WeakReference
-import javax.inject.Inject
-import javax.inject.Named
-import javax.inject.Singleton
-
-fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
-
-@Singleton
-class PrivacyItemController @Inject constructor(
-        val context: Context,
-        private val appOpsController: AppOpsController,
-        @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler,
-        @Named(BG_HANDLER_NAME) private val bgHandler: Handler
-) : Dumpable {
-
-    @VisibleForTesting
-    internal companion object {
-        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO,
-                AppOpsManager.OP_COARSE_LOCATION,
-                AppOpsManager.OP_FINE_LOCATION)
-        val intents = listOf(Intent.ACTION_USER_FOREGROUND,
-                Intent.ACTION_MANAGED_PROFILE_ADDED,
-                Intent.ACTION_MANAGED_PROFILE_REMOVED)
-        const val TAG = "PrivacyItemController"
-        const val SYSTEM_UID = 1000
-        const val MSG_ADD_CALLBACK = 0
-        const val MSG_REMOVE_CALLBACK = 1
-        const val MSG_UPDATE_LISTENING_STATE = 2
-    }
-
-    @VisibleForTesting
-    internal var privacyList = emptyList<PrivacyItem>()
-        @Synchronized get() = field.toList() // Returns a shallow copy of the list
-        @Synchronized set
-
-    private val userManager = context.getSystemService(UserManager::class.java)
-    private var currentUserIds = emptyList<Int>()
-    private var listening = false
-    val systemApp =
-            PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
-    private val callbacks = mutableListOf<WeakReference<Callback>>()
-    private val messageHandler = H(WeakReference(this), uiHandler.looper)
-
-    private val notifyChanges = Runnable {
-        val list = privacyList
-        callbacks.forEach { it.get()?.privacyChanged(list) }
-    }
-
-    private val updateListAndNotifyChanges = Runnable {
-        updatePrivacyList()
-        uiHandler.post(notifyChanges)
-    }
-
-    private var indicatorsAvailable = isPermissionsHubEnabled()
-    @VisibleForTesting
-    internal val devicePropertyChangedListener =
-            object : DeviceConfig.OnPropertyChangedListener {
-        override fun onPropertyChanged(namespace: String, name: String, value: String?) {
-            if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace) &&
-                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(name)) {
-                indicatorsAvailable = java.lang.Boolean.parseBoolean(value)
-                messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
-                messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
-            }
-        }
-    }
-
-    private val cb = object : AppOpsController.Callback {
-        override fun onActiveStateChanged(
-            code: Int,
-            uid: Int,
-            packageName: String,
-            active: Boolean
-        ) {
-            val userId = UserHandle.getUserId(uid)
-            if (userId in currentUserIds) {
-                update(false)
-            }
-        }
-    }
-
-    @VisibleForTesting
-    internal var userSwitcherReceiver = Receiver()
-        set(value) {
-            context.unregisterReceiver(field)
-            field = value
-            registerReceiver()
-        }
-
-    init {
-        DeviceConfig.addOnPropertyChangedListener(
-                DeviceConfig.NAMESPACE_PRIVACY, context.mainExecutor, devicePropertyChangedListener)
-    }
-
-    private fun unregisterReceiver() {
-        context.unregisterReceiver(userSwitcherReceiver)
-    }
-
-    private fun registerReceiver() {
-        context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
-            intents.forEach {
-                addAction(it)
-            }
-        }, null, null)
-    }
-
-    private fun update(updateUsers: Boolean) {
-        if (updateUsers) {
-            val currentUser = ActivityManager.getCurrentUser()
-            currentUserIds = userManager.getProfiles(currentUser).map { it.id }
-        }
-        bgHandler.post(updateListAndNotifyChanges)
-    }
-
-    /**
-     * Updates listening status based on whether there are callbacks and the indicators are enabled
-     *
-     * This is only called from private (add/remove)Callback and from the config listener, all in
-     * main thread.
-     */
-    private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and indicatorsAvailable
-        if (listening == listen) return
-        listening = listen
-        if (listening) {
-            appOpsController.addCallback(OPS, cb)
-            registerReceiver()
-            update(true)
-        } else {
-            appOpsController.removeCallback(OPS, cb)
-            unregisterReceiver()
-            // Make sure that we remove all indicators and notify listeners if we are not
-            // listening anymore due to indicators being disabled
-            update(false)
-        }
-    }
-
-    private fun addCallback(callback: WeakReference<Callback>) {
-        callbacks.add(callback)
-        if (callbacks.isNotEmpty() && !listening) {
-            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
-            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
-        }
-        // Notify this callback if we didn't set to listening
-        else if (listening) uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
-    }
-
-    private fun removeCallback(callback: WeakReference<Callback>) {
-        // Removes also if the callback is null
-        callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
-        if (callbacks.isEmpty()) {
-            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
-            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
-        }
-    }
-
-    fun addCallback(callback: Callback) {
-        messageHandler.obtainMessage(MSG_ADD_CALLBACK, callback).sendToTarget()
-    }
-
-    fun removeCallback(callback: Callback) {
-        messageHandler.obtainMessage(MSG_REMOVE_CALLBACK, callback).sendToTarget()
-    }
-
-    private fun updatePrivacyList() {
-        if (!listening) {
-            privacyList = emptyList()
-            return
-        }
-        val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
-                .mapNotNull { toPrivacyItem(it) }.distinct()
-        privacyList = list
-    }
-
-    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
-        val type: PrivacyType = when (appOpItem.code) {
-            AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
-            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
-            AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
-            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
-            else -> return null
-        }
-        if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp)
-        val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid, context)
-        return PrivacyItem(type, app)
-    }
-
-    // Used by containing class to get notified of changes
-    interface Callback {
-        fun privacyChanged(privacyItems: List<PrivacyItem>)
-    }
-
-    internal inner class Receiver : BroadcastReceiver() {
-        override fun onReceive(context: Context?, intent: Intent?) {
-            if (intent?.action in intents) {
-                update(true)
-            }
-        }
-    }
-
-    private class NotifyChangesToCallback(
-        private val callback: Callback?,
-        private val list: List<PrivacyItem>
-    ) : Runnable {
-        override fun run() {
-            callback?.privacyChanged(list)
-        }
-    }
-
-    override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) {
-        pw?.println("PrivacyItemController state:")
-        pw?.println("  Listening: $listening")
-        pw?.println("  Current user ids: $currentUserIds")
-        pw?.println("  Privacy Items:")
-        privacyList.forEach {
-            pw?.print("    ")
-            pw?.println(it.toString())
-        }
-        pw?.println("  Callbacks:")
-        callbacks.forEach {
-            it.get()?.let {
-                pw?.print("    ")
-                pw?.println(it.toString())
-            }
-        }
-    }
-
-    private class H(
-            private val outerClass: WeakReference<PrivacyItemController>,
-            looper: Looper
-    ) : Handler(looper) {
-        override fun handleMessage(msg: Message) {
-            super.handleMessage(msg)
-            when (msg.what) {
-                MSG_UPDATE_LISTENING_STATE -> outerClass.get()?.setListeningState()
-
-                MSG_ADD_CALLBACK -> {
-                    if (msg.obj !is PrivacyItemController.Callback) return
-                    outerClass.get()?.addCallback(
-                            WeakReference(msg.obj as PrivacyItemController.Callback))
-                }
-
-                MSG_REMOVE_CALLBACK -> {
-                    if (msg.obj !is PrivacyItemController.Callback) return
-                    outerClass.get()?.removeCallback(
-                            WeakReference(msg.obj as PrivacyItemController.Callback))
-                }
-                else -> {}
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 96533bc..a28b60d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -32,30 +32,24 @@
 import android.graphics.Rect;
 import android.media.AudioManager;
 import android.os.Handler;
-import android.os.Looper;
 import android.provider.AlarmClock;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
-import android.util.StatsLog;
 import android.view.ContextThemeWrapper;
 import android.view.DisplayCutout;
 import android.view.View;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
-import android.widget.Space;
 import android.widget.TextView;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DualToneHandler;
@@ -63,11 +57,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.privacy.PrivacyDialogBuilder;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyItemControllerKt;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -78,8 +67,6 @@
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 
@@ -121,7 +108,6 @@
     private TintedIconManager mIconManager;
     private TouchAnimator mStatusIconsAlphaAnimator;
     private TouchAnimator mHeaderTextContainerAlphaAnimator;
-    private TouchAnimator mPrivacyChipAlphaAnimator;
     private DualToneHandler mDualToneHandler;
 
     private View mSystemIconsView;
@@ -141,12 +127,7 @@
     private View mRingerContainer;
     private Clock mClockView;
     private DateView mDateView;
-    private OngoingPrivacyChip mPrivacyChip;
-    private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
-    private boolean mPermissionsHubEnabled;
-
-    private PrivacyItemController mPrivacyItemController;
 
     private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
         @Override
@@ -156,41 +137,17 @@
         }
     };
     private boolean mHasTopCutout = false;
-    private boolean mPrivacyChipLogged = false;
-
-    private final DeviceConfig.OnPropertyChangedListener mPropertyListener =
-            new DeviceConfig.OnPropertyChangedListener() {
-                @Override
-                public void onPropertyChanged(String namespace, String name, String value) {
-                    if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace)
-                            && SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(
-                            name)) {
-                        mPermissionsHubEnabled = Boolean.valueOf(value);
-                        StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
-                        iconContainer.setIgnoredSlots(getIgnoredIconSlots());
-                    }
-                }
-            };
-
-    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
-        @Override
-        public void privacyChanged(List<PrivacyItem> privacyItems) {
-            mPrivacyChip.setPrivacyList(privacyItems);
-            setChipVisibility(!privacyItems.isEmpty());
-        }
-    };
 
     @Inject
     public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
             NextAlarmController nextAlarmController, ZenModeController zenModeController,
             StatusBarIconController statusBarIconController,
-            ActivityStarter activityStarter, PrivacyItemController privacyItemController) {
+            ActivityStarter activityStarter) {
         super(context, attrs);
         mAlarmController = nextAlarmController;
         mZenController = zenModeController;
         mStatusBarIconController = statusBarIconController;
         mActivityStarter = activityStarter;
-        mPrivacyItemController = privacyItemController;
         mDualToneHandler = new DualToneHandler(
                 new ContextThemeWrapper(context, R.style.QSHeaderTheme));
     }
@@ -203,8 +160,6 @@
         mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
         StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
-        // Ignore privacy icons because they show in the space above QQS
-        iconContainer.addIgnoredSlots(getIgnoredIconSlots());
         iconContainer.setShouldRestrictIcons(false);
         mIconManager = new TintedIconManager(iconContainer);
 
@@ -218,9 +173,6 @@
         mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
         mRingerModeTextView = findViewById(R.id.ringer_mode_text);
         mRingerContainer = findViewById(R.id.ringer_container);
-        mRingerContainer.setOnClickListener(this::onClick);
-        mPrivacyChip = findViewById(R.id.privacy_chip);
-        mPrivacyChip.setOnClickListener(this::onClick);
         mCarrierGroup = findViewById(R.id.carrier_group);
 
 
@@ -243,7 +195,6 @@
         mClockView = findViewById(R.id.clock);
         mClockView.setOnClickListener(this);
         mDateView = findViewById(R.id.date);
-        mSpace = findViewById(R.id.space);
 
         // Tint for the battery icons are handled in setupHost()
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -254,26 +205,6 @@
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
-
-        mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled();
-        // Change the ignored slots when DeviceConfig flag changes
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
-                mContext.getMainExecutor(), mPropertyListener);
-
-    }
-
-    private List<String> getIgnoredIconSlots() {
-        ArrayList<String> ignored = new ArrayList<>();
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_camera));
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_microphone));
-        if (mPermissionsHubEnabled) {
-            ignored.add(mContext.getResources().getString(
-                    com.android.internal.R.string.status_bar_location));
-        }
-
-        return ignored;
     }
 
     private void updateStatusText() {
@@ -287,21 +218,6 @@
         }
     }
 
-    private void setChipVisibility(boolean chipVisible) {
-        if (chipVisible && mPermissionsHubEnabled) {
-            mPrivacyChip.setVisibility(View.VISIBLE);
-            // Makes sure that the chip is logged as viewed at most once each time QS is opened
-            // mListening makes sure that the callback didn't return after the user closed QS
-            if (!mPrivacyChipLogged && mListening) {
-                mPrivacyChipLogged = true;
-                StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED,
-                        StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_VIEWED);
-            }
-        } else {
-            mPrivacyChip.setVisibility(View.GONE);
-        }
-    }
-
     private boolean updateRingerStatus() {
         boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
         CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -408,7 +324,6 @@
 
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
-        updatePrivacyChipAlphaAnimator();
     }
 
     private void updateStatusIconAlphaAnimator() {
@@ -423,12 +338,6 @@
                 .build();
     }
 
-    private void updatePrivacyChipAlphaAnimator() {
-        mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
-                .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
-                .build();
-    }
-
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -467,10 +376,6 @@
                 mHeaderTextContainerView.setVisibility(INVISIBLE);
             }
         }
-        if (mPrivacyChipAlphaAnimator != null) {
-            mPrivacyChip.setExpanded(expansionFraction > 0.5);
-            mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
-        }
     }
 
     public void disable(int state1, int state2, boolean animate) {
@@ -503,21 +408,6 @@
             mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
 
         }
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
-        if (cutout != null) {
-            Rect topCutout = cutout.getBoundingRectTop();
-            if (topCutout.isEmpty()) {
-                mHasTopCutout = false;
-                lp.width = 0;
-                mSpace.setVisibility(View.GONE);
-            } else {
-                mHasTopCutout = true;
-                lp.width = topCutout.width();
-                mSpace.setVisibility(View.VISIBLE);
-            }
-        }
-        mSpace.setLayoutParams(lp);
-        setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
         return super.onApplyWindowInsets(insets);
     }
 
@@ -542,13 +432,10 @@
             mAlarmController.addCallback(this);
             mContext.registerReceiver(mRingerReceiver,
                     new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
-            mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenController.removeCallback(this);
             mAlarmController.removeCallback(this);
-            mPrivacyItemController.removeCallback(mPICCallback);
             mContext.unregisterReceiver(mRingerReceiver);
-            mPrivacyChipLogged = false;
         }
     }
 
@@ -566,18 +453,6 @@
                 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                         AlarmClock.ACTION_SHOW_ALARMS), 0);
             }
-        } else if (v == mPrivacyChip) {
-            // Makes sure that the builder is grabbed as soon as the chip is pressed
-            PrivacyDialogBuilder builder = mPrivacyChip.getBuilder();
-            if (builder.getAppsAndTypes().size() == 0) return;
-            Handler mUiHandler = new Handler(Looper.getMainLooper());
-            StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED,
-                    StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_CLICKED);
-            mUiHandler.post(() -> {
-                mActivityStarter.postStartActivityDismissingKeyguard(
-                        new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
-                mHost.collapsePanels();
-            });
         } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 3f1ff33..4597b16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -43,7 +43,6 @@
     private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
             "heads_up_status_bar_view_super_parcelable";
     private static final String FIRST_LAYOUT = "first_layout";
-    private static final String PUBLIC_MODE = "public_mode";
     private static final String VISIBILITY = "visibility";
     private static final String ALPHA = "alpha";
     private int mAbsoluteStartPadding;
@@ -54,7 +53,6 @@
     private Rect mLayoutedIconRect = new Rect();
     private int[] mTmpPosition = new int[2];
     private boolean mFirstLayout = true;
-    private boolean mPublicMode;
     private int mMaxWidth;
     private View mRootView;
     private int mSysWinInset;
@@ -121,7 +119,6 @@
         bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
                 super.onSaveInstanceState());
         bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
-        bundle.putBoolean(PUBLIC_MODE, mPublicMode);
         bundle.putInt(VISIBILITY, getVisibility());
         bundle.putFloat(ALPHA, getAlpha());
 
@@ -139,7 +136,6 @@
         Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
         super.onRestoreInstanceState(superState);
         mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
-        mPublicMode = bundle.getBoolean(PUBLIC_MODE, false);
         if (bundle.containsKey(VISIBILITY)) {
             setVisibility(bundle.getInt(VISIBILITY));
         }
@@ -166,11 +162,13 @@
         if (entry != null) {
             mShowingEntry = entry;
             CharSequence text = entry.headsUpStatusBarText;
-            if (mPublicMode) {
+            if (entry.isSensitive()) {
                 text = entry.headsUpStatusBarTextPublic;
             }
             mTextView.setText(text);
-        } else {
+            mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
+        } else if (mShowingEntry != null){
+            mShowingEntry.setOnSensitiveChangedListener(null);
             mShowingEntry = null;
         }
     }
@@ -273,10 +271,6 @@
         mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
     }
 
-    public void setPublicMode(boolean publicMode) {
-        mPublicMode = publicMode;
-    }
-
     public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
         mOnDrawingRectChangedListener = onDrawingRectChangedListener;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0b3c6a5..787cc97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -161,7 +161,7 @@
             boolean deviceSensitive = devicePublic
                     && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
                     currentUserId);
-            ent.getRow().setSensitive(sensitive, deviceSensitive);
+            ent.setSensitive(sensitive, deviceSensitive);
             ent.getRow().setNeedsRedaction(needsRedaction);
             if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
                 NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index bdc4d2a..986486a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -36,9 +36,12 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.HeadsUpManager
 
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -51,7 +54,9 @@
 class PulseExpansionHandler @Inject
 constructor(context: Context,
             private val wakeUpCoordinator: NotificationWakeUpCoordinator,
-            private val bypassController: KeyguardBypassController) : Gefingerpoken {
+            private val bypassController: KeyguardBypassController,
+            private val headsUpManager: HeadsUpManagerPhone,
+            private val roundnessManager: NotificationRoundnessManager) : Gefingerpoken {
     companion object {
         private val RUBBERBAND_FACTOR_STATIC = 0.25f
         private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
@@ -67,9 +72,20 @@
             val changed = field != value
             field = value
             bypassController.isPulseExpanding = value
-            if (changed && !value && !leavingLockscreen) {
-                bypassController.maybePerformPendingUnlock()
-                pulseExpandAbortListener?.run()
+            if (changed) {
+                if (value) {
+                    val topEntry = headsUpManager.topEntry
+                    topEntry?.let {
+                        roundnessManager.setTrackingHeadsUp(it.row)
+                    }
+                } else {
+                    roundnessManager.setTrackingHeadsUp(null)
+                    if (!leavingLockscreen) {
+                        bypassController.maybePerformPendingUnlock()
+                        pulseExpandAbortListener?.run()
+                    }
+                }
+                headsUpManager.unpinAll(true /* userUnPinned */)
             }
         }
     var leavingLockscreen: Boolean = false
@@ -231,7 +247,7 @@
     }
 
     private fun captureStartingChild(x: Float, y: Float) {
-        if (mStartingChild == null) {
+        if (mStartingChild == null && !bypassController.bypassEnabled) {
             mStartingChild = findView(x, y)
             if (mStartingChild != null) {
                 setUserLocked(mStartingChild!!, true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 95af9fd..6a3816c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -38,7 +38,7 @@
 class NotificationWakeUpCoordinator @Inject constructor(
         private val mContext: Context,
         private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
-        private val mStatusBarStateController: StatusBarStateController,
+        private val statusBarStateController: StatusBarStateController,
         private val bypassController: KeyguardBypassController)
     : OnHeadsUpChangedListener, StatusBarStateController.StateListener {
 
@@ -69,6 +69,8 @@
     private val mDozeParameters: DozeParameters
     private var pulseExpanding: Boolean = false
     private val wakeUpListeners = arrayListOf<WakeUpListener>()
+    private var state: Int = StatusBarState.KEYGUARD
+
     var fullyAwake: Boolean = false
 
     var willWakeUp = false
@@ -111,14 +113,14 @@
             if (bypassController.bypassEnabled) {
                 // We also allow pulsing on the lock screen!
                 canShow = canShow || (mWakingUp || willWakeUp || fullyAwake)
-                        && mStatusBarStateController.state == StatusBarState.KEYGUARD
+                        && statusBarStateController.state == StatusBarState.KEYGUARD
             }
             return canShow
         }
 
     init {
         mHeadsUpManagerPhone.addListener(this)
-        mStatusBarStateController.addCallback(this)
+        statusBarStateController.addCallback(this)
         mDozeParameters = DozeParameters.getInstance(mContext)
     }
 
@@ -217,13 +219,21 @@
 
     override fun onStateChanged(newState: Int) {
         updateDozeAmountIfBypass();
+        if (bypassController.bypassEnabled &&
+                newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED
+                && (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
+            // We're leaving shade locked. Let's animate the notifications away
+            setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
+            setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
+        }
+        this.state = newState
     }
 
     private fun updateDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
             var amount = 1.0f;
-            if (mStatusBarStateController.state == StatusBarState.SHADE
-                    || mStatusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+            if (statusBarStateController.state == StatusBarState.SHADE
+                    || statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
                 amount = 0.0f;
             }
             setDozeAmount(amount,  amount)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index aacb22d..fe56552 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -115,7 +115,20 @@
         view.setTag(animationEndTag, newEndValue);
     }
 
-    public static <T extends View> boolean isAnimating(T view, AnimatableProperty property) {
-        return  view.getTag(property.getAnimatorTag()) != null;
+    public static <T extends View> void applyImmediately(T view, AnimatableProperty property,
+            float newValue) {
+        cancelAnimation(view, property);
+        property.getProperty().set(view, newValue);
+    }
+
+    public static void cancelAnimation(View view, AnimatableProperty property) {
+        ValueAnimator animator = (ValueAnimator) view.getTag(property.getAnimatorTag());
+        if (animator != null) {
+            animator.cancel();
+        }
+    }
+
+    public static boolean isAnimating(View view, AnimatableProperty property) {
+        return view.getTag(property.getAnimatorTag()) != null;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 9184dec..9db715d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -173,6 +173,8 @@
      * the lock screen/status bar and in the top section in the shade.
      */
     private boolean mHighPriority;
+    private boolean mSensitive = true;
+    private Runnable mOnSensitiveChangedListener;
 
     public NotificationEntry(StatusBarNotification n) {
         this(n, null);
@@ -859,6 +861,30 @@
         return Objects.equals(n.category, category);
     }
 
+    /**
+     * Set this notification to be sensitive.
+     *
+     * @param sensitive true if the content of this notification is sensitive right now
+     * @param deviceSensitive true if the device in general is sensitive right now
+     */
+    public void setSensitive(boolean sensitive, boolean deviceSensitive) {
+        getRow().setSensitive(sensitive, deviceSensitive);
+        if (sensitive != mSensitive) {
+            mSensitive = sensitive;
+            if (mOnSensitiveChangedListener != null) {
+                mOnSensitiveChangedListener.run();
+            }
+        }
+    }
+
+    public boolean isSensitive() {
+        return mSensitive;
+    }
+
+    public void setOnSensitiveChangedListener(Runnable listener) {
+        mOnSensitiveChangedListener = listener;
+    }
+
     /** Information about a suggestion that is being edited. */
     public static class EditedSuggestionInfo {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6d9d5ec..dca152f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -1040,6 +1040,10 @@
         return false;
     }
 
+    public int getHeadsUpHeightWithoutHeader() {
+        return getHeight();
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0ca8814..ae534d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2637,7 +2637,7 @@
 
 
     private int getHeadsUpHeight() {
-        return getShowingLayout().getHeadsUpHeight();
+        return getShowingLayout().getHeadsUpHeight(false /* forceNoHeader */);
     }
 
     public boolean areGutsExposed() {
@@ -2775,6 +2775,17 @@
     }
 
     @Override
+    public int getHeadsUpHeightWithoutHeader() {
+        if (!canShowHeadsUp() || !mIsHeadsUp) {
+            return getCollapsedHeight();
+        }
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return mChildrenContainer.getCollapsedHeightWithoutHeader();
+        }
+        return getShowingLayout().getHeadsUpHeight(true /* forceNoHeader */);
+    }
+
+    @Override
     public void setClipTopAmount(int clipTopAmount) {
         super.setClipTopAmount(clipTopAmount);
         for (NotificationContentView l : mLayouts) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index d6b7cf3..90f6324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -713,11 +713,15 @@
     }
 
     private int getViewHeight(int visibleType) {
+        return getViewHeight(visibleType, false /* forceNoHeader */);
+    }
+
+    private int getViewHeight(int visibleType, boolean forceNoHeader) {
         View view = getViewForVisibleType(visibleType);
         int height = view.getHeight();
         NotificationViewWrapper viewWrapper = getWrapperForView(view);
         if (viewWrapper != null) {
-            height += viewWrapper.getHeaderTranslation();
+            height += viewWrapper.getHeaderTranslation(forceNoHeader);
         }
         return height;
     }
@@ -1748,14 +1752,15 @@
         return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
     }
 
-    public int getHeadsUpHeight() {
+    public int getHeadsUpHeight(boolean forceNoHeader) {
         int viewType = VISIBLE_TYPE_HEADSUP;
         if (mHeadsUpChild == null) {
             viewType = VISIBLE_TYPE_CONTRACTED;
         }
         // The headsUp remote input quickly switches to the expanded one, so lets also include that
         // one
-        return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
+        return getViewHeight(viewType, forceNoHeader)
+                + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
                 + getExtraRemoteInputHeight(mExpandedRemoteInput);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 7ebdb93..97d8443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -51,7 +51,7 @@
  */
 public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
 
-    private final int mTranslationForHeader;
+    private final int mFullHeaderTranslation;
     protected ImageView mPicture;
     private ProgressBar mProgressBar;
     private TextView mTitle;
@@ -135,7 +135,7 @@
                     }
 
                 }, TRANSFORMING_VIEW_TEXT);
-        mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+        mFullHeaderTranslation = ctx.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin)
                 - ctx.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_top);
@@ -340,20 +340,20 @@
 
             // We also need to compensate for any header translation, since we're always at the end.
             mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
-                    - getHeaderTranslation());
+                    - getHeaderTranslation(false /* forceNoHeader */));
         }
     }
 
     @Override
-    public int getHeaderTranslation() {
-        return (int) mHeaderTranslation;
+    public int getHeaderTranslation(boolean forceNoHeader) {
+        return forceNoHeader ? mFullHeaderTranslation : (int) mHeaderTranslation;
     }
 
     @Override
     public void setHeaderVisibleAmount(float headerVisibleAmount) {
         super.setHeaderVisibleAmount(headerVisibleAmount);
         mNotificationHeader.setAlpha(headerVisibleAmount);
-        mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+        mHeaderTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
         mView.setTranslationY(mHeaderTranslation);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 3808702..47906a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -224,7 +224,7 @@
         return null;
     }
 
-    public int getHeaderTranslation() {
+    public int getHeaderTranslation(boolean forceNoHeader) {
         return 0;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 1242431..45f7b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1067,6 +1067,11 @@
                 false /* likeHighPriority */);
     }
 
+    public int getCollapsedHeightWithoutHeader() {
+        return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
+                false /* likeHighPriority */, 0);
+    }
+
     /**
      * Get the minimum Height for this group.
      *
@@ -1074,10 +1079,22 @@
      * @param likeHighPriority if the height should be calculated as if it were not low priority
      */
     private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
+        return getMinHeight(maxAllowedVisibleChildren, likeHighPriority, mCurrentHeaderTranslation);
+    }
+
+    /**
+     * Get the minimum Height for this group.
+     *
+     * @param maxAllowedVisibleChildren the number of children that should be visible
+     * @param likeHighPriority if the height should be calculated as if it were not low priority
+     * @param headerTranslation the translation amount of the header
+     */
+    private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority,
+            int headerTranslation) {
         if (!likeHighPriority && showingAsLowPriority()) {
             return mNotificationHeaderLowPriority.getHeight();
         }
-        int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+        int minExpandHeight = mNotificationHeaderMargin + headerTranslation;
         int visibleChildren = 0;
         boolean firstChild = true;
         int childCount = mChildren.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index d8ccd3a..4221846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,10 +18,14 @@
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
+
+import android.util.MathUtils;
+
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.HashSet;
@@ -33,12 +37,13 @@
  * A class that manages the roundness for notification views
  */
 @Singleton
-class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
 
     private final ActivatableNotificationView[] mFirstInSectionViews;
     private final ActivatableNotificationView[] mLastInSectionViews;
     private final ActivatableNotificationView[] mTmpFirstInSectionViews;
     private final ActivatableNotificationView[] mTmpLastInSectionViews;
+    private final KeyguardBypassController mBypassController;
     private boolean mExpanded;
     private HashSet<ExpandableView> mAnimatedChildren;
     private Runnable mRoundingChangedCallback;
@@ -46,11 +51,12 @@
     private float mAppearFraction;
 
     @Inject
-    NotificationRoundnessManager() {
+    NotificationRoundnessManager(KeyguardBypassController keyguardBypassController) {
         mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
         mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
+        mBypassController = keyguardBypassController;
     }
 
     @Override
@@ -124,18 +130,18 @@
         if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
             return 1.0f;
         }
-        if (view.showingPulsing()) {
-            return 1.0f;
-        }
         if (isFirstInSection(view, true /* include first section */) && top) {
             return 1.0f;
         }
         if (isLastInSection(view, true /* include last section */) && !top) {
             return 1.0f;
         }
-        if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+        if (view == mTrackedHeadsUp) {
             // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
             // rounded.
+            return MathUtils.saturate(1.0f - mAppearFraction);
+        }
+        if (view.showingPulsing() && !mBypassController.getBypassEnabled()) {
             return 1.0f;
         }
         return 0.0f;
@@ -232,6 +238,10 @@
     }
 
     public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        ExpandableNotificationRow previous = mTrackedHeadsUp;
         mTrackedHeadsUp = row;
+        if (previous != null) {
+            updateView(previous, true /* animate */);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 44e5aef..e2cb8d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -466,12 +466,10 @@
     private int mCornerRadius;
     private int mSidePaddings;
     private final Rect mBackgroundAnimationRect = new Rect();
-    private int mAntiBurnInOffsetX;
     private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
     private int mHeadsUpInset;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private NotificationIconAreaController mIconAreaController;
-    private float mHorizontalPanelTranslation;
     private final NotificationLockscreenUserManager mLockscreenUserManager =
             Dependency.get(NotificationLockscreenUserManager.class);
     private final Rect mTmpRect = new Rect();
@@ -500,6 +498,8 @@
             mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
     private final NotificationSectionsManager mSectionsManager;
     private boolean mAnimateBottomOnLayout;
+    private float mLastSentAppear;
+    private float mLastSentExpandedHeight;
 
     @Inject
     public NotificationStackScrollLayout(
@@ -561,13 +561,13 @@
                 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
         mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
-        addOnExpandedHeightListener(mRoundnessManager::setExpanded);
+        addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
         setOutlineProvider(mOutlineProvider);
 
         // Blocking helper manager wants to know the expanded state, update as well.
         NotificationBlockingHelperManager blockingHelperManager =
                 Dependency.get(NotificationBlockingHelperManager.class);
-        addOnExpandedHeightListener((height, unused) -> {
+        addOnExpandedHeightChangedListener((height, unused) -> {
             blockingHelperManager.setNotificationShadeExpanded(height);
         });
 
@@ -634,7 +634,11 @@
     public float getWakeUpHeight() {
         ActivatableNotificationView firstChild = getFirstChildWithBackground();
         if (firstChild != null) {
-            return firstChild.getCollapsedHeight();
+            if (mKeyguardBypassController.getBypassEnabled()) {
+                return firstChild.getHeadsUpHeightWithoutHeader();
+            } else {
+                return firstChild.getCollapsedHeight();
+            }
         }
         return 0f;
     }
@@ -1306,7 +1310,7 @@
                 stackHeight = (int) height;
             }
         } else {
-            appearFraction = getAppearFraction(height);
+            appearFraction = calculateAppearFraction(height);
             if (appearFraction >= 0) {
                 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
                         appearFraction);
@@ -1329,9 +1333,26 @@
             requestChildrenUpdate();
         }
         setStackTranslation(translationY);
-        for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
-            BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
-            listener.accept(mExpandedHeight, appearFraction);
+        notifyAppearChangedListeners();
+    }
+
+    private void notifyAppearChangedListeners() {
+        float appear;
+        float expandAmount;
+        if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
+            appear = calculateAppearFractionBypass();
+            expandAmount = getPulseHeight();
+        } else {
+            appear = MathUtils.saturate(calculateAppearFraction(mExpandedHeight));
+            expandAmount = mExpandedHeight;
+        }
+        if (appear != mLastSentAppear || expandAmount != mLastSentExpandedHeight) {
+            mLastSentAppear = appear;
+            mLastSentExpandedHeight = expandAmount;
+            for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+                BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+                listener.accept(expandAmount, appear);
+            }
         }
     }
 
@@ -1453,7 +1474,7 @@
      * @return the fraction of the appear animation that has been performed
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    public float getAppearFraction(float height) {
+    public float calculateAppearFraction(float height) {
         float appearEndPosition = getAppearEndPosition();
         float appearStartPosition = getAppearStartPosition();
         return (height - appearStartPosition)
@@ -4707,17 +4728,6 @@
         notifyHeightChangeListener(mShelf);
     }
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void updatePanelTranslation() {
-        setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedHideAmount);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
-        mHorizontalPanelTranslation = verticalPanelTranslation;
-        updatePanelTranslation();
-    }
-
     /**
      * Sets the current hide amount.
      *
@@ -4737,7 +4747,7 @@
         boolean nowFullyHidden = mAmbientState.isFullyHidden();
         boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
         if (nowFullyHidden != wasFullyHidden) {
-            setVisibility(mAmbientState.isFullyHidden() ? View.INVISIBLE : View.VISIBLE);
+            updateVisibility();
         }
         if (!wasHiddenAtAll && nowHiddenAtAll) {
             resetExposedMenuView(true /* animate */, true /* animate */);
@@ -4747,10 +4757,14 @@
         }
         updateAlgorithmHeightAndPadding();
         updateBackgroundDimming();
-        updatePanelTranslation();
         requestChildrenUpdate();
     }
 
+    private void updateVisibility() {
+        boolean shouldShow = !mAmbientState.isFullyHidden() || !onKeyguard();
+        setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void notifyHideAnimationStart(boolean hide) {
         // We only swap the scaling factor if we're fully hidden or fully awake to avoid
@@ -5241,7 +5255,7 @@
         boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
 
         if (mHeadsUpAppearanceController != null) {
-            mHeadsUpAppearanceController.setPublicMode(publicMode);
+            mHeadsUpAppearanceController.onStateChanged();
         }
 
         SysuiStatusBarStateController state = (SysuiStatusBarStateController)
@@ -5259,6 +5273,7 @@
         onUpdateRowStates();
 
         mEntryManager.updateNotifications();
+        updateVisibility();
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5297,12 +5312,6 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
-        mAntiBurnInOffsetX = antiBurnInOffsetX;
-        updatePanelTranslation();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
                         + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
@@ -5360,13 +5369,13 @@
     }
 
     /**
-     * Add a listener whenever the expanded height changes. The first value passed as an argument
-     * is the expanded height and the second one is the appearFraction.
+     * Add a listener whenever the expanded height changes. The first value passed as an
+     * argument is the expanded height and the second one is the appearFraction.
      *
      * @param listener the listener to notify.
      */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+    public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
         mExpandedHeightListeners.add(listener);
     }
 
@@ -5374,7 +5383,7 @@
      * Stop a listener from listening to the expandedHeight.
      */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+    public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
         mExpandedHeightListeners.remove(listener);
     }
 
@@ -5595,6 +5604,9 @@
      */
     public float setPulseHeight(float height) {
         mAmbientState.setPulseHeight(height);
+        if (mKeyguardBypassController.getBypassEnabled()) {
+            notifyAppearChangedListeners();
+        }
         requestChildrenUpdate();
         return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
     }
@@ -5654,6 +5666,16 @@
         mAmbientState.setOnPulseHeightChangedListener(listener);
     }
 
+    public float calculateAppearFractionBypass() {
+        float pulseHeight = getPulseHeight();
+        float wakeUpHeight = getWakeUpHeight();
+        float dragDownAmount = pulseHeight - wakeUpHeight;
+
+        // The total distance required to fully reveal the header
+        float totalDistance = getIntrinsicPadding();
+        return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 58f457e..d655b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -35,6 +35,7 @@
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -201,20 +202,21 @@
     }
 
     protected int adjustDisableFlags(int state) {
+        boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
+        if (headsUpVisible) {
+            state |= DISABLE_CLOCK;
+        }
+
         if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
                 && !mKeyguardMonitor.isKeyguardFadingAway()
-                && shouldHideNotificationIcons()) {
+                && shouldHideNotificationIcons()
+                && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+                        && headsUpVisible)) {
             state |= DISABLE_NOTIFICATION_ICONS;
             state |= DISABLE_SYSTEM_INFO;
             state |= DISABLE_CLOCK;
         }
 
-        // In landscape, the heads up show but shouldHideNotificationIcons() return false
-        // because the visual icon is in notification icon area rather than heads up's space.
-        // whether the notification icon show or not, clock should hide when heads up show.
-        if (mStatusBarComponent.isHeadsUpShouldBeVisible()) {
-            state |= DISABLE_CLOCK;
-        }
 
         if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
             if (mNetworkController.hasEmergencyCryptKeeperText()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index a243855..60e381a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -149,6 +149,14 @@
         mHandler.removeCallbacks(mPulseOut);
     }
 
+    /**
+     * When pulsing, cancel any timeouts that would take you out of the pulsing state.
+     */
+    public void cancelPendingPulseTimeout() {
+        mHandler.removeCallbacks(mPulseOut);
+        mHandler.removeCallbacks(mPulseOutExtended);
+    }
+
     private void cancelPulsing() {
         if (mPulseCallback != null) {
             if (DEBUG) Log.d(TAG, "Cancel pulsing");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 66903fa..46dd5e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.DisplayCutout;
@@ -27,11 +29,16 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.function.BiConsumer;
@@ -56,13 +63,16 @@
     private final Consumer<ExpandableNotificationRow>
             mSetTrackingHeadsUp = this::setTrackingHeadsUp;
     private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
-    private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
+    private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
+    private final KeyguardBypassController mBypassController;
+    private final StatusBarStateController mStatusBarStateController;
+    private final CommandQueue mCommandQueue;
     @VisibleForTesting
     float mExpandedHeight;
     @VisibleForTesting
     boolean mIsExpanded;
     @VisibleForTesting
-    float mExpandFraction;
+    float mAppearFraction;
     private ExpandableNotificationRow mTrackedChild;
     private boolean mShown;
     private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
@@ -77,13 +87,17 @@
             };
     private boolean mAnimationsEnabled = true;
     Point mPoint;
+    private KeyguardMonitor mKeyguardMonitor;
 
 
     public HeadsUpAppearanceController(
             NotificationIconAreaController notificationIconAreaController,
             HeadsUpManagerPhone headsUpManager,
-            View statusbarView) {
-        this(notificationIconAreaController, headsUpManager,
+            View statusbarView,
+            SysuiStatusBarStateController statusBarStateController,
+            KeyguardBypassController keyguardBypassController) {
+        this(notificationIconAreaController, headsUpManager, statusBarStateController,
+                keyguardBypassController,
                 statusbarView.findViewById(R.id.heads_up_status_bar_view),
                 statusbarView.findViewById(R.id.notification_stack_scroller),
                 statusbarView.findViewById(R.id.notification_panel),
@@ -96,6 +110,8 @@
     public HeadsUpAppearanceController(
             NotificationIconAreaController notificationIconAreaController,
             HeadsUpManagerPhone headsUpManager,
+            StatusBarStateController stateController,
+            KeyguardBypassController bypassController,
             HeadsUpStatusBarView headsUpStatusBarView,
             NotificationStackScrollLayout stackScroller,
             NotificationPanelView panelView,
@@ -114,7 +130,7 @@
         panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
         panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
         panelView.setHeadsUpAppearanceController(this);
-        mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
+        mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
         mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mStackScroller.setHeadsUpAppearanceController(this);
         mClockView = clockView;
@@ -135,6 +151,10 @@
                 mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
             }
         });
+        mBypassController = bypassController;
+        mStatusBarStateController = stateController;
+        mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class);
+        mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
     }
 
 
@@ -144,7 +164,7 @@
         mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
         mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
         mPanelView.setHeadsUpAppearanceController(null);
-        mStackScroller.removeOnExpandedHeightListener(mSetExpandedHeight);
+        mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
         mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mDarkIconDispatcher.removeDarkReceiver(this);
     }
@@ -219,7 +239,7 @@
 
     private void updateTopEntry() {
         NotificationEntry newEntry = null;
-        if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
+        if (shouldBeVisible()) {
             newEntry = mHeadsUpManager.getTopEntry();
         }
         NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
@@ -342,7 +362,13 @@
      * @return if the heads up status bar view should be shown
      */
     public boolean shouldBeVisible() {
-        return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
+        boolean canShow = !mIsExpanded;
+        if (mBypassController.getBypassEnabled() &&
+                (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+                        || mKeyguardMonitor.isKeyguardGoingAway())) {
+            canShow = true;
+        }
+        return canShow && mHeadsUpManager.hasPinnedHeadsUp();
     }
 
     @Override
@@ -351,12 +377,24 @@
         updateHeader(entry);
     }
 
-    public void setExpandedHeight(float expandedHeight, float appearFraction) {
-        boolean changedHeight = expandedHeight != mExpandedHeight;
+    @Override
+    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+        if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
+            // Show the status bar icons when the pinned mode changes
+            mCommandQueue.recomputeDisableFlags(
+                    mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+        }
+    }
+
+    public void setAppearFraction(float expandedHeight, float appearFraction) {
+        boolean changed = expandedHeight != mExpandedHeight;
         mExpandedHeight = expandedHeight;
-        mExpandFraction = appearFraction;
+        mAppearFraction = appearFraction;
         boolean isExpanded = expandedHeight > 0;
-        if (changedHeight) {
+        // We only notify if the expandedHeight changed and not on the appearFraction, since
+        // otherwise we may run into an infinite loop where the panel and this are constantly
+        // updating themselves over just a small fraction
+        if (changed) {
             updateHeadsUpHeaders();
         }
         if (isExpanded != mIsExpanded) {
@@ -389,8 +427,9 @@
     public void updateHeader(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
-        if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
-            headerVisibleAmount = mExpandFraction;
+        if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild
+                || row.showingPulsing()) {
+            headerVisibleAmount = mAppearFraction;
         }
         row.setHeaderVisibleAmount(headerVisibleAmount);
     }
@@ -400,8 +439,7 @@
         mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
     }
 
-    public void setPublicMode(boolean publicMode) {
-        mHeadsUpStatusBarView.setPublicMode(publicMode);
+    public void onStateChanged() {
         updateTopEntry();
     }
 
@@ -410,7 +448,7 @@
             mTrackedChild = oldController.mTrackedChild;
             mExpandedHeight = oldController.mExpandedHeight;
             mIsExpanded = oldController.mIsExpanded;
-            mExpandFraction = oldController.mExpandFraction;
+            mAppearFraction = oldController.mAppearFraction;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index bec655c..ade855e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -66,6 +66,7 @@
     @VisibleForTesting
     final int mExtensionTime;
     private final StatusBarStateController mStatusBarStateController;
+    private final KeyguardBypassController mBypassController;
     private View mStatusBarWindowView;
     private NotificationGroupManager mGroupManager;
     private VisualStabilityManager mVisualStabilityManager;
@@ -113,7 +114,8 @@
 
     @Inject
     public HeadsUpManagerPhone(@NonNull final Context context,
-            StatusBarStateController statusBarStateController) {
+            StatusBarStateController statusBarStateController,
+            KeyguardBypassController bypassController) {
         super(context);
         Resources resources = mContext.getResources();
         mAutoDismissNotificationDecayDozing = resources.getInteger(
@@ -121,6 +123,7 @@
         mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
         mStatusBarStateController = statusBarStateController;
         mStatusBarStateController.addCallback(this);
+        mBypassController = bypassController;
 
         initResources();
     }
@@ -231,6 +234,17 @@
         mStatusBarState = newState;
     }
 
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        if (!isDozing) {
+            // Let's make sure all huns we got while dozing time out within the normal timeout
+            // duration. Otherwise they could get stuck for a very long time
+            for (AlertEntry entry : mAlertEntries.values()) {
+                entry.updateEntry(true /* updatePostTime */);
+            }
+        }
+    }
+
     /**
      * Set that we are exiting the headsUp pinned mode, but some notifications might still be
      * animating out. This is used to keep the touchable regions in a sane state.
@@ -412,8 +426,11 @@
 
     @Override
     protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
-        return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
-                || super.shouldHeadsUpBecomePinned(entry);
+        boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsExpanded;
+        if (mBypassController.getBypassEnabled()) {
+            pin |= mStatusBarState == StatusBarState.KEYGUARD;
+        }
+        return pin || super.shouldHeadsUpBecomePinned(entry);
     }
 
     @Override
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 58d1462..3f1cb6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.content.Context
+import android.content.pm.PackageManager
 import android.hardware.biometrics.BiometricSourceType
 import android.hardware.face.FaceManager
 import android.provider.Settings
@@ -73,6 +74,9 @@
                 }
             }
         })
+        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            return
+        }
         val faceManager = context.getSystemService(FaceManager::class.java)
         if (faceManager?.isHardwareDetected != true) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index ca001c6..1e7c44c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.annotation.IntDef;
@@ -29,12 +28,12 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricSourceType;
-import android.os.Handler;
 import android.os.Trace;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.Nullable;
@@ -43,6 +42,7 @@
 import com.android.internal.telephony.IccCardConstants;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -67,7 +67,7 @@
 public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChangedListener,
         StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
         UnlockMethodCache.OnUnlockMethodChangedListener,
-        NotificationWakeUpCoordinator.WakeUpListener {
+        NotificationWakeUpCoordinator.WakeUpListener, ViewTreeObserver.OnPreDrawListener {
 
     private static final int STATE_LOCKED = 0;
     private static final int STATE_LOCK_OPEN = 1;
@@ -79,12 +79,12 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final AccessibilityController mAccessibilityController;
     private final DockManager mDockManager;
-    private final Handler mMainHandler;
     private final KeyguardMonitor mKeyguardMonitor;
     private final KeyguardBypassController mBypassController;
     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
 
     private int mLastState = 0;
+    private boolean mForceUpdate;
     private boolean mTransientBiometricsError;
     private boolean mIsFaceUnlockState;
     private boolean mSimLocked;
@@ -92,23 +92,20 @@
     private boolean mPulsing;
     private boolean mDozing;
     private boolean mDocked;
-    private boolean mLastDozing;
-    private boolean mLastPulsing;
     private int mIconColor;
     private float mDozeAmount;
-    private int mIconRes;
     private boolean mBouncerShowing;
-    private boolean mWasPulsingOnThisFrame;
     private boolean mWakeAndUnlockRunning;
     private boolean mKeyguardShowing;
     private boolean mShowingLaunchAffordance;
+    private boolean mUpdatePending;
 
     private final KeyguardMonitor.Callback mKeyguardMonitorCallback =
             new KeyguardMonitor.Callback() {
                 @Override
                 public void onKeyguardShowingChanged() {
                     mKeyguardShowing = mKeyguardMonitor.isShowing();
-                    update(false /* force */);
+                    update();
                 }
             };
     private final DockManager.DockEventListener mDockEventListener =
@@ -119,7 +116,7 @@
                             || event == DockManager.STATE_DOCKED_HIDE;
                     if (docked != mDocked) {
                         mDocked = docked;
-                        update(true /* force */);
+                        update();
                     }
         }
     };
@@ -129,9 +126,8 @@
                 @Override
                 public void onSimStateChanged(int subId, int slotId,
                         IccCardConstants.State simState) {
-                    boolean oldSimLocked = mSimLocked;
                     mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
-                    update(oldSimLocked != mSimLocked);
+                    update();
                 }
 
                 @Override
@@ -149,11 +145,6 @@
                 public void onStrongAuthStateChanged(int userId) {
                     update();
                 }
-
-                @Override
-                public void onBiometricsCleared() {
-                    update();
-                }
     };
 
     @Inject
@@ -164,8 +155,7 @@
             KeyguardBypassController bypassController,
             NotificationWakeUpCoordinator wakeUpCoordinator,
             KeyguardMonitor keyguardMonitor,
-            @Nullable DockManager dockManager,
-            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+            @Nullable DockManager dockManager) {
         super(context, attrs);
         mContext = context;
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
@@ -177,7 +167,6 @@
         mWakeUpCoordinator = wakeUpCoordinator;
         mKeyguardMonitor = keyguardMonitor;
         mDockManager = dockManager;
-        mMainHandler = mainHandler;
     }
 
     @Override
@@ -194,6 +183,7 @@
             mDockManager.addListener(mDockEventListener);
         }
         onThemeChanged();
+        update();
     }
 
     @Override
@@ -247,51 +237,61 @@
     }
 
     public void update(boolean force) {
-        int state = getState();
-        mIsFaceUnlockState = state == STATE_SCANNING_FACE;
-        if (state != mLastState || mLastDozing != mDozing || mLastPulsing != mPulsing || force) {
-            @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(mLastState,
-                    state, mLastPulsing, mPulsing, mLastDozing, mDozing);
-            boolean isAnim = lockAnimIndex != -1;
-
-            int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
-            if (iconRes != mIconRes) {
-                mIconRes = iconRes;
-
-                Drawable icon = mContext.getDrawable(iconRes);
-                final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
-                        ? (AnimatedVectorDrawable) icon
-                        : null;
-                setImageDrawable(icon, false);
-                if (mIsFaceUnlockState) {
-                    announceForAccessibility(getContext().getString(
-                            R.string.accessibility_scanning_face));
-                }
-
-                if (animation != null && isAnim) {
-                    animation.forceAnimationOnUI();
-                    animation.clearAnimationCallbacks();
-                    animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
-                        @Override
-                        public void onAnimationEnd(Drawable drawable) {
-                            if (getDrawable() == animation && state == getState()
-                                    && doesAnimationLoop(lockAnimIndex)) {
-                                animation.start();
-                            } else {
-                                Trace.endAsyncSection("LockIcon#Animation", state);
-                            }
-                        }
-                    });
-                    Trace.beginAsyncSection("LockIcon#Animation", state);
-                    animation.start();
-                }
-            }
-            updateDarkTint();
-
-            mLastState = state;
-            mLastDozing = mDozing;
-            mLastPulsing = mPulsing;
+        if (force) {
+            mForceUpdate = true;
         }
+        if (!mUpdatePending) {
+            mUpdatePending = true;
+            getViewTreeObserver().addOnPreDrawListener(this);
+        }
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        mUpdatePending = false;
+        getViewTreeObserver().removeOnPreDrawListener(this);
+
+        int state = getState();
+        int lastState = mLastState;
+        mIsFaceUnlockState = state == STATE_SCANNING_FACE;
+        mLastState = state;
+
+        if (lastState != state || mForceUpdate) {
+            mForceUpdate = false;
+            @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState,
+                    state, mPulsing, mDozing);
+            boolean isAnim = lockAnimIndex != -1;
+            int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
+
+            Drawable icon = mContext.getDrawable(iconRes);
+            final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+                    ? (AnimatedVectorDrawable) icon
+                    : null;
+            setImageDrawable(icon, false);
+            if (mIsFaceUnlockState) {
+                announceForAccessibility(getContext().getString(
+                        R.string.accessibility_scanning_face));
+            }
+
+            if (animation != null && isAnim) {
+                animation.forceAnimationOnUI();
+                animation.clearAnimationCallbacks();
+                animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
+                    @Override
+                    public void onAnimationEnd(Drawable drawable) {
+                        if (getDrawable() == animation && state == getState()
+                                && doesAnimationLoop(lockAnimIndex)) {
+                            animation.start();
+                        } else {
+                            Trace.endAsyncSection("LockIcon#Animation", state);
+                        }
+                    }
+                });
+                Trace.beginAsyncSection("LockIcon#Animation", state);
+                animation.start();
+            }
+        }
+        updateDarkTint();
 
         boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
         boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
@@ -302,8 +302,25 @@
                 && !mBouncerShowing) {
             invisible = true;
         }
-        setVisibility(invisible ? INVISIBLE : VISIBLE);
+        boolean wasInvisible = getVisibility() == INVISIBLE;
+        if (invisible != wasInvisible) {
+            setVisibility(invisible ? INVISIBLE : VISIBLE);
+            animate().cancel();
+            if (!invisible) {
+                setScaleX(0);
+                setScaleY(0);
+                animate()
+                        .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+                        .scaleX(1)
+                        .scaleY(1)
+                        .withLayer()
+                        .setDuration(233)
+                        .start();
+            }
+        }
         updateClickability();
+
+        return true;
     }
 
     private void updateClickability() {
@@ -364,30 +381,22 @@
         return lockAnimIndex == SCANNING;
     }
 
-    private int getAnimationIndexForTransition(int oldState, int newState,
-            boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing) {
+    private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+            boolean dozing) {
 
         // Never animate when screen is off
-        if (dozing && !pulsing && !mWasPulsingOnThisFrame) {
+        if (dozing && !pulsing) {
             return -1;
         }
 
-        boolean isError = oldState != STATE_BIOMETRICS_ERROR && newState == STATE_BIOMETRICS_ERROR;
-        boolean justUnlocked = oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN;
-        boolean justLocked = oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED;
-        boolean nowPulsing = !wasPulsing && pulsing;
-        boolean turningOn = wasDozing && !dozing && !mWasPulsingOnThisFrame;
-
-        if (isError) {
+        if (newState == STATE_BIOMETRICS_ERROR) {
             return ERROR;
-        } else if (justUnlocked) {
+        } else if (oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN) {
             return UNLOCK;
-        } else if (justLocked) {
+        } else if (oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED) {
             return LOCK;
         } else if (newState == STATE_SCANNING_FACE) {
             return SCANNING;
-        } else if ((nowPulsing || turningOn) && newState != STATE_LOCK_OPEN) {
-            return LOCK_IN;
         }
         return -1;
     }
@@ -407,37 +416,33 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ERROR, UNLOCK, LOCK, SCANNING, LOCK_IN})
+    @IntDef({ERROR, UNLOCK, LOCK, SCANNING})
     @interface LockAnimIndex {}
-    private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3, LOCK_IN = 4;
+    private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
     private static final int[][] LOCK_ANIM_RES_IDS = new int[][] {
             {
                     R.anim.lock_to_error,
                     R.anim.lock_unlock,
                     R.anim.lock_lock,
-                    R.anim.lock_scanning,
-                    R.anim.lock_in,
+                    R.anim.lock_scanning
             },
             {
                     R.anim.lock_to_error_circular,
                     R.anim.lock_unlock_circular,
                     R.anim.lock_lock_circular,
-                    R.anim.lock_scanning_circular,
-                    R.anim.lock_in_circular,
+                    R.anim.lock_scanning_circular
             },
             {
                     R.anim.lock_to_error_filled,
                     R.anim.lock_unlock_filled,
                     R.anim.lock_lock_filled,
-                    R.anim.lock_scanning_filled,
-                    R.anim.lock_in_filled,
+                    R.anim.lock_scanning_filled
             },
             {
                     R.anim.lock_to_error_rounded,
                     R.anim.lock_unlock_rounded,
                     R.anim.lock_lock_rounded,
-                    R.anim.lock_scanning_rounded,
-                    R.anim.lock_in_rounded,
+                    R.anim.lock_scanning_rounded
             },
     };
 
@@ -462,7 +467,7 @@
             return STATE_LOCK_OPEN;
         } else if (mTransientBiometricsError) {
             return STATE_BIOMETRICS_ERROR;
-        } else if (updateMonitor.isFaceDetectionRunning()) {
+        } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) {
             return STATE_SCANNING_FACE;
         } else {
             return STATE_LOCKED;
@@ -481,12 +486,6 @@
      */
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
-        if (!mPulsing) {
-            mWasPulsingOnThisFrame = true;
-            mMainHandler.post(() -> {
-                mWasPulsingOnThisFrame = false;
-            });
-        }
         update();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 7b1d1c6..0bbfbef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -435,18 +435,6 @@
     }
 
     @Override
-    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
-    }
-
-    @Override
-    public void onHeadsUpPinned(NotificationEntry entry) {
-    }
-
-    @Override
-    public void onHeadsUpUnPinned(NotificationEntry entry) {
-    }
-
-    @Override
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 6f9a6ec..cd97722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -13,7 +13,6 @@
 import androidx.annotation.NonNull;
 import androidx.collection.ArrayMap;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.settingslib.Utils;
@@ -24,7 +23,6 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -242,11 +240,15 @@
     protected boolean shouldShowNotificationIcon(NotificationEntry entry,
             boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
             boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon,
-            boolean hidePulsing) {
+            boolean hidePulsing, boolean onlyShowCenteredIcon) {
 
-        final boolean isCenteredNotificationIcon = entry.centeredIcon != null
+        final boolean isCenteredNotificationIcon = mCenteredIconView != null
+                && entry.centeredIcon != null
                 && Objects.equals(entry.centeredIcon, mCenteredIconView);
-        if (hideCenteredIcon == isCenteredNotificationIcon) {
+        if (onlyShowCenteredIcon) {
+            return isCenteredNotificationIcon;
+        }
+        if (hideCenteredIcon && isCenteredNotificationIcon) {
             return false;
         }
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -299,8 +301,9 @@
                 false /* hideDismissed */,
                 false /* hideRepliedMessages */,
                 false /* hideCurrentMedia */,
-                true /* hide centered icon */,
-                false /* hidePulsing */);
+                false /* hide centered icon */,
+                false /* hidePulsing */,
+                false /* onlyShowCenteredIcon */);
     }
 
     public void updateStatusBarIcons() {
@@ -311,7 +314,8 @@
                 true /* hideRepliedMessages */,
                 false /* hideCurrentMedia */,
                 true /* hide centered icon */,
-                false /* hidePulsing */);
+                false /* hidePulsing */,
+                false /* onlyShowCenteredIcon */);
     }
 
     private void updateCenterIcon() {
@@ -322,7 +326,8 @@
                 false /* hideRepliedMessages */,
                 false /* hideCurrentMedia */,
                 false /* hide centered icon */,
-                false /* hidePulsing */);
+                false /* hidePulsing */,
+                true/* onlyShowCenteredIcon */);
     }
 
     public void updateAodNotificationIcons() {
@@ -333,7 +338,8 @@
                 true /* hideRepliedMessages */,
                 true /* hideCurrentMedia */,
                 true /* hide centered icon */,
-                mBypassController.getBypassEnabled() /* hidePulsing */);
+                mBypassController.getBypassEnabled() /* hidePulsing */,
+                false /* onlyShowCenteredIcon */);
     }
 
     /**
@@ -349,7 +355,7 @@
     private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
             NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
             boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia,
-            boolean hideCenteredIcon, boolean hidePulsing) {
+            boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -359,7 +365,8 @@
             if (view instanceof ExpandableNotificationRow) {
                 NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
                 if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
-                        hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing)) {
+                        hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing,
+                        onlyShowCenteredIcon)) {
                     StatusBarIconView iconView = function.apply(ent);
                     if (iconView != null) {
                         toShow.add(iconView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 4acf7b2..a53ce9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -320,7 +320,7 @@
         if (child instanceof StatusBarIconView) {
             boolean isReplacingIcon = isReplacingIcon(child);
             final StatusBarIconView icon = (StatusBarIconView) child;
-            if (mAnimationsEnabled && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
+            if (areAnimationsEnabled(icon) && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                     && child.getVisibility() == VISIBLE && isReplacingIcon) {
                 int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
                 if (mAddAnimationStartIndex < 0) {
@@ -331,7 +331,7 @@
             }
             if (!mChangingViewPositions) {
                 mIconStates.remove(child);
-                if (mAnimationsEnabled && !isReplacingIcon) {
+                if (areAnimationsEnabled(icon) && !isReplacingIcon) {
                     addTransientView(icon, 0);
                     boolean isIsolatedIcon = child == mIsolatedIcon;
                     icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
@@ -342,6 +342,10 @@
         }
     }
 
+    private boolean areAnimationsEnabled(StatusBarIconView icon) {
+        return mAnimationsEnabled || icon == mIsolatedIcon;
+    }
+
     /**
      * Finds the first view with a translation bigger then a given value
      */
@@ -695,7 +699,7 @@
                 StatusBarIconView icon = (StatusBarIconView) view;
                 boolean animate = false;
                 AnimationProperties animationProperties = null;
-                boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation
+                boolean animationsAllowed = areAnimationsEnabled(icon) && !mDisallowNextAnimation
                         && !noAnimations;
                 if (animationsAllowed) {
                     if (justAdded || justReplaced) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 186fd37..32dc96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -148,6 +148,15 @@
 
     private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
             .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+    private static final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT
+            = AnimatableProperty.from("KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+            NotificationPanelView::setKeyguardHeadsUpShowingAmount,
+            NotificationPanelView::getKeyguardHeadsUpShowingAmount,
+            R.id.keyguard_hun_animator_tag,
+            R.id.keyguard_hun_animator_end_tag,
+            R.id.keyguard_hun_animator_start_tag);
+    private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
 
     private final InjectionInflationController mInjectionInflationController;
     private final PowerManager mPowerManager;
@@ -356,6 +365,9 @@
     private Runnable mOnReinflationListener;
     private int mDarkIconSize;
     private int mHeadsUpInset;
+    private boolean mHeadsUpPinnedMode;
+    private float mKeyguardHeadsUpShowingAmount = 0.0f;
+    private boolean mShowingKeyguardHeadsUp;
 
     @Inject
     public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -741,7 +753,6 @@
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
         mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
 
         mStackScrollerMeasuringPass++;
@@ -1415,6 +1426,7 @@
         } else if (oldState == StatusBarState.SHADE_LOCKED
                 && statusBarState == StatusBarState.KEYGUARD) {
             animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            mNotificationStackScroller.resetScrollPosition();
             // Only animate header if the header is visible. If not, it will partially animate out
             // the top of QS
             if (!mQsExpanded) {
@@ -1429,6 +1441,7 @@
                 }
             }
         }
+        updateKeyguardStatusBarForHeadsUp();
         if (keyguardShowing) {
             updateDozingVisibilities(false /* animate */);
         }
@@ -1721,21 +1734,11 @@
         } else {
             int expandedPosition = mClockPositionResult.stackScrollerPadding;
             return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
-                    calculateHeaderAppearAmountBypass());
+                    mNotificationStackScroller.calculateAppearFractionBypass());
         }
     }
 
 
-    private float calculateHeaderAppearAmountBypass() {
-        float pulseHeight = mNotificationStackScroller.getPulseHeight();
-        float wakeUpHeight = mNotificationStackScroller.getWakeUpHeight();
-        float dragDownAmount = pulseHeight - wakeUpHeight;
-
-        // The total distance required to fully reveal the header
-        float totalDistance = mClockPositionResult.stackScrollerPadding;
-        return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
-    }
-
     protected void requestScrollerTopPaddingUpdate(boolean animate) {
         mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
         if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
@@ -2029,7 +2032,8 @@
                 !mHeadsUpManager.hasPinnedHeadsUp()) {
             alpha = getFadeoutAlpha();
         }
-        if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning) {
+        if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
+                && !mKeyguardBypassController.getBypassEnabled()) {
             alpha *= mClockPositionResult.clockAlpha;
         }
         mNotificationStackScroller.setAlpha(alpha);
@@ -2067,7 +2071,7 @@
         if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
             return -mQs.getQsMinExpansionHeight();
         }
-        float appearAmount = mNotificationStackScroller.getAppearFraction(mExpandedHeight);
+        float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
         float startHeight = -mQsExpansionHeight;
         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
                 && mNotificationStackScroller.isPulseExpanding()) {
@@ -2077,7 +2081,7 @@
                 // again after the header has animated away
                 appearAmount = 0;
             } else {
-                appearAmount = calculateHeaderAppearAmountBypass();
+                appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
             }
             startHeight = -mQs.getQsMinExpansionHeight();
         }
@@ -2118,6 +2122,7 @@
         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
         float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
                 * mKeyguardStatusBarAnimateAlpha;
+        newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
         mKeyguardStatusBar.setAlpha(newAlpha);
         mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing ? VISIBLE : INVISIBLE);
     }
@@ -2430,7 +2435,6 @@
             onUnlockHintFinished();
             return;
         }
-        mUpdateMonitor.requestFaceAuth();
         super.startUnlockHintAnimation();
     }
 
@@ -2651,10 +2655,14 @@
         switch (mBarState) {
             case StatusBarState.KEYGUARD:
                 if (!mDozingOnDown) {
-                    mLockscreenGestureLogger.write(
-                            MetricsEvent.ACTION_LS_HINT,
-                            0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
-                    startUnlockHintAnimation();
+                    if (mKeyguardBypassController.getBypassEnabled()) {
+                        mUpdateMonitor.requestFaceAuth();
+                    } else {
+                        mLockscreenGestureLogger.write(
+                                MetricsEvent.ACTION_LS_HINT,
+                                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+                        startUnlockHintAnimation();
+                    }
                 }
                 return true;
             case StatusBarState.SHADE_LOCKED:
@@ -2747,16 +2755,51 @@
                     mHeadsUpExistenceChangedRunnable);
         }
         updateGestureExclusionRect();
+        mHeadsUpPinnedMode = inPinnedMode;
+        updateHeadsUpVisibility();
+        updateKeyguardStatusBarForHeadsUp();
+    }
+
+    private void updateKeyguardStatusBarForHeadsUp() {
+        boolean showingKeyguardHeadsUp = mKeyguardShowing
+                && mHeadsUpAppearanceController.shouldBeVisible();
+        if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+            mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+            if (mKeyguardShowing) {
+                PropertyAnimator.setProperty(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
+                        showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
+                        true /* animate */);
+            } else {
+                PropertyAnimator.applyImmediately(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
+            }
+        }
+    }
+
+    private void setKeyguardHeadsUpShowingAmount(float amount) {
+        mKeyguardHeadsUpShowingAmount = amount;
+        updateHeaderKeyguardAlpha();
+    }
+
+    private float getKeyguardHeadsUpShowingAmount() {
+        return mKeyguardHeadsUpShowingAmount;
     }
 
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
         mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+        updateHeadsUpVisibility();
+    }
+
+    private void updateHeadsUpVisibility() {
+        ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
     }
 
     @Override
     public void onHeadsUpPinned(NotificationEntry entry) {
-        mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
+        if (!isOnKeyguard()) {
+            mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
+                    true);
+        }
     }
 
     @Override
@@ -2765,7 +2808,7 @@
         // When we're unpinning the notification via active edge they remain heads-upped,
         // we need to make sure that an animation happens in this case, otherwise the notification
         // will stick to the top without any interaction.
-        if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+        if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
             mNotificationStackScroller.generateHeadsUpAnimation(
                     entry.getHeadsUpAnimationView(), false);
             entry.setHeadsUpIsVisible();
@@ -2832,7 +2875,7 @@
     }
 
     protected void setHorizontalPanelTranslation(float translation) {
-        mNotificationStackScroller.setHorizontalPanelTranslation(translation);
+        mNotificationStackScroller.setTranslationX(translation);
         mQsFrame.setTranslationX(translation);
         int size = mVerticalTranslationListener.size();
         for (int i = 0; i < size; i++) {
@@ -2888,7 +2931,8 @@
 
     @Override
     protected boolean isPanelVisibleBecauseOfHeadsUp() {
-        return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
+        return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
+                && mBarState == StatusBarState.SHADE;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 65b0ecc..063d00b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -107,8 +107,12 @@
         return mExpanded;
     }
 
-    private void updateVisibility() {
-        mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+    protected void updateVisibility() {
+        mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+    }
+
+    protected boolean shouldPanelBeVisible() {
+        return mExpanded || mBouncerShowing;
     }
 
     public boolean panelEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index ee43879..17c200e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -42,10 +42,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.UiOffloadThread;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyItemControllerKt;
-import com.android.systemui.privacy.PrivacyType;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.statusbar.CommandQueue;
@@ -66,9 +62,6 @@
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -83,12 +76,12 @@
                 ZenModeController.Callback,
                 DeviceProvisionedListener,
                 KeyguardMonitor.Callback,
-                PrivacyItemController.Callback,
                 LocationController.LocationChangeCallback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    public static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
+    public static final int LOCATION_STATUS_ICON_ID =
+            com.android.internal.R.drawable.perm_group_location;
 
     private final String mSlotCast;
     private final String mSlotHotspot;
@@ -102,8 +95,6 @@
     private final String mSlotHeadset;
     private final String mSlotDataSaver;
     private final String mSlotLocation;
-    private final String mSlotMicrophone;
-    private final String mSlotCamera;
     private final String mSlotSensorsOff;
 
     private final Context mContext;
@@ -121,7 +112,6 @@
     private final DeviceProvisionedController mProvisionedController;
     private final KeyguardMonitor mKeyguardMonitor;
     private final LocationController mLocationController;
-    private final PrivacyItemController mPrivacyItemController;
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
     private final SensorPrivacyController mSensorPrivacyController;
 
@@ -154,7 +144,6 @@
         mProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mLocationController = Dependency.get(LocationController.class);
-        mPrivacyItemController = Dependency.get(PrivacyItemController.class);
         mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
 
         mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
@@ -170,8 +159,6 @@
         mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
         mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
         mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
-        mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
-        mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
         mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
 
         // listen for broadcasts
@@ -231,13 +218,6 @@
                 context.getString(R.string.accessibility_data_saver_on));
         mIconController.setIconVisibility(mSlotDataSaver, false);
 
-        // privacy items
-        mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
-                PrivacyType.TYPE_MICROPHONE.getName(mContext));
-        mIconController.setIconVisibility(mSlotMicrophone, false);
-        mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
-                PrivacyType.TYPE_CAMERA.getName(mContext));
-        mIconController.setIconVisibility(mSlotCamera, false);
         mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                 mContext.getString(R.string.accessibility_location_active));
         mIconController.setIconVisibility(mSlotLocation, false);
@@ -257,7 +237,6 @@
         mNextAlarmController.addCallback(mNextAlarmCallback);
         mDataSaver.addCallback(this);
         mKeyguardMonitor.addCallback(this);
-        mPrivacyItemController.addCallback(this);
         mSensorPrivacyController.addCallback(mSensorPrivacyListener);
         mLocationController.addCallback(this);
 
@@ -601,46 +580,9 @@
         mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
     }
 
-    @Override  // PrivacyItemController.Callback
-    public void privacyChanged(List<PrivacyItem> privacyItems) {
-        updatePrivacyItems(privacyItems);
-    }
-
-    private void updatePrivacyItems(List<PrivacyItem> items) {
-        boolean showCamera = false;
-        boolean showMicrophone = false;
-        boolean showLocation = false;
-        for (PrivacyItem item : items) {
-            if (item == null /* b/124234367 */) {
-                if (DEBUG) {
-                    Log.e(TAG, "updatePrivacyItems - null item found");
-                    StringWriter out = new StringWriter();
-                    mPrivacyItemController.dump(null, new PrintWriter(out), null);
-                    Log.e(TAG, out.toString());
-                }
-                continue;
-            }
-            switch (item.getPrivacyType()) {
-                case TYPE_CAMERA:
-                    showCamera = true;
-                    break;
-                case TYPE_LOCATION:
-                    showLocation = true;
-                    break;
-                case TYPE_MICROPHONE:
-                    showMicrophone = true;
-                    break;
-            }
-        }
-
-        mIconController.setIconVisibility(mSlotCamera, showCamera);
-        mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        mIconController.setIconVisibility(mSlotLocation, showLocation);
-    }
-
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!PrivacyItemControllerKt.isPermissionsHubEnabled()) updateLocation();
+        updateLocation();
     }
 
     // Updates the status view based on the current state of location requests.
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 68eba50..660810f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -46,6 +46,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.Objects;
 
@@ -82,6 +83,7 @@
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
      */
     private int mCutoutSideNudge = 0;
+    private boolean mHeadsUpVisible;
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -379,4 +381,14 @@
         }
         return null;
     }
+
+    public void setHeadsUpVisible(boolean headsUpVisible) {
+        mHeadsUpVisible = headsUpVisible;
+        updateVisibility();
+    }
+
+    @Override
+    protected boolean shouldPanelBeVisible() {
+        return mHeadsUpVisible || super.shouldPanelBeVisible();
+    }
 }
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 213475f..7db4afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -847,7 +847,8 @@
                         mHeadsUpAppearanceController.destroy();
                     }
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
-                            mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
+                            mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
+                            mStatusBarStateController, mKeyguardBypassController);
                     mHeadsUpAppearanceController.readFrom(oldController);
                     mStatusBarWindow.setStatusBarView(mStatusBarView);
                     updateAreThereNotifications();
@@ -1537,10 +1538,16 @@
                 });
             }
         } else {
-            if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) {
+            boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
+                    && mState == StatusBarState.KEYGUARD;
+            if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()
+                    || bypassKeyguard) {
                 // We are currently tracking or is open and the shade doesn't need to be kept
                 // open artificially.
                 mStatusBarWindowController.setHeadsUpShowing(false);
+                if (bypassKeyguard) {
+                    mStatusBarWindowController.setForceStatusBarVisible(false);
+                }
             } else {
                 // we need to keep the panel open artificially, let's wait until the animation
                 // is finished.
@@ -1557,20 +1564,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationEntry entry) {
-        dismissVolumeDialog();
-    }
-
-    @Override
-    public void onHeadsUpUnPinned(NotificationEntry entry) {
-    }
-
-    @Override
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mEntryManager.updateNotifications();
-        if (isDozing()) {
-            if (isHeadsUp) {
-                mDozeServiceHost.fireNotificationPulse();
+        if (isDozing() && isHeadsUp) {
+            mDozeServiceHost.fireNotificationPulse();
+            if (mPulsing) {
+                mDozeScrimController.cancelPendingPulseTimeout();
             }
         }
         if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
@@ -1671,7 +1670,7 @@
         }
     }
 
-    public boolean isHeadsUpShouldBeVisible() {
+    public boolean headsUpShouldBeVisible() {
         return mHeadsUpAppearanceController.shouldBeVisible();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index 78eb394..f36c56f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -190,6 +190,11 @@
         public void onKeyguardVisibilityChanged(boolean showing) {
             update(false /* updateAlways */);
         }
+
+        @Override
+        public void onBiometricsCleared() {
+            update(false /* alwaysUpdate */);
+        }
     };
 
     public boolean isTrustManaged() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index bebc20b..775a3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -31,6 +31,11 @@
     static final String REASON_WRAP = "wrap";
 
     /**
+     * Default wake-lock timeout, to avoid battery regressions.
+     */
+    long DEFAULT_MAX_TIMEOUT = 20000;
+
+    /**
      * @param why A tag that will be saved for sysui dumps.
      * @see android.os.PowerManager.WakeLock#acquire()
      **/
@@ -46,7 +51,14 @@
     Runnable wrap(Runnable r);
 
     static WakeLock createPartial(Context context, String tag) {
-        return wrap(createPartialInner(context, tag));
+        return createPartial(context, tag, DEFAULT_MAX_TIMEOUT);
+    }
+
+    /**
+     * Creates a {@link WakeLock} that has a default release timeout.
+     * @see android.os.PowerManager.WakeLock#acquire(long) */
+    static WakeLock createPartial(Context context, String tag, long maxTimeout) {
+        return wrap(createPartialInner(context, tag), maxTimeout);
     }
 
     @VisibleForTesting
@@ -66,7 +78,14 @@
         };
     }
 
-    static WakeLock wrap(final PowerManager.WakeLock inner) {
+    /**
+     * Create a {@link WakeLock} containing a {@link PowerManager.WakeLock}.
+     * @param inner To be wrapped.
+     * @param maxTimeout When to expire.
+     * @return The new wake lock.
+     */
+    @VisibleForTesting
+    static WakeLock wrap(final PowerManager.WakeLock inner, long maxTimeout) {
         return new WakeLock() {
             private final HashMap<String, Integer> mActiveClients = new HashMap<>();
 
@@ -74,7 +93,7 @@
             public void acquire(String why) {
                 mActiveClients.putIfAbsent(why, 0);
                 mActiveClients.put(why, mActiveClients.get(why) + 1);
-                inner.acquire();
+                inner.acquire(maxTimeout);
             }
 
             /** @see PowerManager.WakeLock#release() */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 540ac84..59d5c24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,20 +16,17 @@
 
 package com.android.systemui.appops;
 
-import static junit.framework.TestCase.assertFalse;
-
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
 import android.content.pm.PackageManager;
@@ -39,6 +36,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -54,7 +52,6 @@
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = UserHandle.getUid(0, 0);
     private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
-    private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
 
     @Mock
     private AppOpsManager mAppOpsManager;
@@ -64,32 +61,16 @@
     private AppOpsController.Callback mCallback;
     @Mock
     private AppOpsControllerImpl.H mMockHandler;
-    @Mock
-    private PermissionFlagsCache mFlagsCache;
 
     private AppOpsControllerImpl mController;
-    private TestableLooper mTestableLooper;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTestableLooper = TestableLooper.get(this);
 
         getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
 
-        // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
-        // TEST_UID_NON_USER_SENSITIVE are user sensitive.
-        getContext().setMockPackageManager(mPackageManager);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(UserHandle.getUserHandleForUid(TEST_UID)))).thenReturn(
-                PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(UserHandle.getUserHandleForUid(TEST_UID_OTHER)))).thenReturn(
-                PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(UserHandle.getUserHandleForUid(TEST_UID_NON_USER_SENSITIVE)))).thenReturn(0);
-
-        mController = new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mFlagsCache);
+        mController = new AppOpsControllerImpl(mContext, Dependency.get(Dependency.BG_LOOPER));
     }
 
     @Test
@@ -113,7 +94,6 @@
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 AppOpsManager.MODE_ALLOWED);
-        mTestableLooper.processAllMessages();
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
     }
@@ -123,7 +103,6 @@
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        mTestableLooper.processAllMessages();
         verify(mCallback, never()).onActiveStateChanged(
                 anyInt(), anyInt(), anyString(), anyBoolean());
     }
@@ -134,7 +113,6 @@
         mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        mTestableLooper.processAllMessages();
         verify(mCallback, never()).onActiveStateChanged(
                 anyInt(), anyInt(), anyString(), anyBoolean());
     }
@@ -145,7 +123,6 @@
         mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        mTestableLooper.processAllMessages();
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
     }
@@ -185,14 +162,6 @@
     }
 
     @Test
-    public void nonUserSensitiveOpsAreIgnored() {
-        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
-                TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
-        assertEquals(0, mController.getActiveAppOpsForUser(
-                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
-    }
-
-    @Test
     public void opNotedScheduledForRemoval() {
         mController.setBGHandler(mMockHandler);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
deleted file mode 100644
index dc070de..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 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.appops
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PermissionFlagsCacheTest : SysuiTestCase() {
-
-    companion object {
-        const val TEST_PERMISSION = "test_permission"
-        const val TEST_PACKAGE = "test_package"
-    }
-
-    @Mock
-    private lateinit var mPackageManager: PackageManager
-    @Mock
-    private lateinit var mUserHandle: UserHandle
-    private lateinit var flagsCache: TestPermissionFlagsCache
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        mContext.setMockPackageManager(mPackageManager)
-        flagsCache = TestPermissionFlagsCache(mContext)
-    }
-
-    @Test
-    fun testCallsPackageManager_exactlyOnce() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        flagsCache.time = CACHE_EXPIRATION - 1
-        verify(mPackageManager).getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-    }
-
-    @Test
-    fun testCallsPackageManager_cacheExpired() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        flagsCache.time = CACHE_EXPIRATION + 1
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        verify(mPackageManager, times(2))
-                .getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-    }
-
-    @Test
-    fun testCallsPackageMaanger_multipleKeys() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
-        flagsCache.getPermissionFlags(TEST_PERMISSION, "", mUserHandle)
-        verify(mPackageManager, times(2))
-                .getPermissionFlags(anyString(), anyString(), any())
-    }
-
-    private class TestPermissionFlagsCache(context: Context) : PermissionFlagsCache(context) {
-        var time = 0L
-
-        override fun getCurrentTime(): Long {
-            return time
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
new file mode 100644
index 0000000..d011e48
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 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.classifier.brightline;
+
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClassifierTest extends SysuiTestCase {
+
+    private FalsingDataProvider mDataProvider;
+    private List<MotionEvent> mMotionEvents = new ArrayList<>();
+    private float mOffsetX = 0;
+    private float mOffsetY = 0;
+
+    @Before
+    public void setup() {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.xdpi = 100;
+        displayMetrics.ydpi = 100;
+        displayMetrics.widthPixels = 1000;
+        displayMetrics.heightPixels = 1000;
+        mDataProvider = new FalsingDataProvider(displayMetrics);
+    }
+
+    @After
+    public void tearDown() {
+        resetDataProvider();
+    }
+
+    FalsingDataProvider getDataProvider() {
+        return mDataProvider;
+    }
+
+    void setOffsetX(float offsetX) {
+        mOffsetX = offsetX;
+    }
+
+    void setOffsetY(float offsetY) {
+        mOffsetY = offsetY;
+    }
+
+    void resetDataProvider() {
+        for (MotionEvent motionEvent : mMotionEvents) {
+            motionEvent.recycle();
+        }
+
+        mMotionEvents.clear();
+
+        mDataProvider.onSessionEnd();
+    }
+
+    MotionEvent appendDownEvent(float x, float y) {
+        return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y);
+    }
+
+    MotionEvent appendDownEvent(float x, float y, long eventTime) {
+        return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y, eventTime);
+    }
+
+    MotionEvent appendMoveEvent(float x, float y) {
+        return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y);
+    }
+
+    MotionEvent appendMoveEvent(float x, float y, long eventTime) {
+        return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y, eventTime);
+    }
+
+
+    MotionEvent appendUpEvent(float x, float y) {
+        return appendMotionEvent(MotionEvent.ACTION_UP, x, y);
+    }
+
+    MotionEvent appendUpEvent(float x, float y, long eventTime) {
+        return appendMotionEvent(MotionEvent.ACTION_UP, x, y, eventTime);
+    }
+
+    private MotionEvent appendMotionEvent(int actionType, float x, float y) {
+
+        long eventTime = mMotionEvents.isEmpty() ? 1 : mMotionEvents.get(
+                mMotionEvents.size() - 1).getEventTime() + 1;
+        return appendMotionEvent(actionType, x, y, eventTime);
+    }
+
+    private MotionEvent appendMotionEvent(int actionType, float x, float y, long eventTime) {
+        x += mOffsetX;
+        y += mOffsetY;
+
+        MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, actionType, x, y,
+                0);
+        mMotionEvents.add(motionEvent);
+
+        mDataProvider.onMotionEvent(motionEvent);
+
+        return motionEvent;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
index ade5f36..b45d3f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
@@ -28,8 +28,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -39,7 +38,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class DiagonalClassifierTest extends SysuiTestCase {
+public class DiagonalClassifierTest extends ClassifierTest {
 
     // Next variable is not actually five, but is very close. 5 degrees is currently the value
     // used in the diagonal classifier, so we want slightly less than that to deal with
@@ -57,10 +56,16 @@
 
     @Before
     public void setup() {
+        super.setup();
         MockitoAnnotations.initMocks(this);
         mClassifier = new DiagonalClassifier(mDataProvider);
     }
 
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
     @Test
     public void testPass_UnknownAngle() {
         when(mDataProvider.getAngle()).thenReturn(Float.MAX_VALUE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
index 3d0471b..805bb91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
@@ -18,48 +18,37 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class DistanceClassifierTest extends SysuiTestCase {
+public class DistanceClassifierTest extends ClassifierTest {
 
-    @Mock
     private FalsingDataProvider mDataProvider;
     private FalsingClassifier mClassifier;
-    private List<MotionEvent> mMotionEvents = new ArrayList<>();
-
-    private static final float DPI = 100;
-    private static final int SCREEN_SIZE = (int) (DPI * 10);
 
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mDataProvider.getHeightPixels()).thenReturn(SCREEN_SIZE);
-        when(mDataProvider.getWidthPixels()).thenReturn(SCREEN_SIZE);
-        when(mDataProvider.getXdpi()).thenReturn(DPI);
-        when(mDataProvider.getYdpi()).thenReturn(DPI);
+        super.setup();
+        mDataProvider = getDataProvider();
         mClassifier = new DistanceClassifier(mDataProvider);
     }
 
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
     @Test
     public void testPass_noPointer() {
         assertThat(mClassifier.isFalseTouch(), is(true));
@@ -67,104 +56,54 @@
 
     @Test
     public void testPass_fling() {
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 40, 0);
 
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventB);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 2));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventC);
+        mClassifier.onTouchEvent(appendUpEvent(1, 40));
         assertThat(mClassifier.isFalseTouch(), is(false));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
     public void testFail_flingShort() {
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 10, 0);
-
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventB);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 2));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventC);
+        mClassifier.onTouchEvent(appendUpEvent(1, 10));
         assertThat(mClassifier.isFalseTouch(), is(true));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
     public void testFail_flingSlowly() {
         // These events, in testing, result in a fling that falls just short of the threshold.
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 15, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, 16, 0);
-        MotionEvent motionEventD = MotionEvent.obtain(1, 300, MotionEvent.ACTION_MOVE, 1, 17, 0);
-        MotionEvent motionEventE = MotionEvent.obtain(1, 301, MotionEvent.ACTION_MOVE, 1, 18, 0);
-        MotionEvent motionEventF = MotionEvent.obtain(1, 500, MotionEvent.ACTION_UP, 1, 19, 0);
 
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventB);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 15, 2));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventC);
-        appendMotionEvent(motionEventD);
-        appendMotionEvent(motionEventE);
-        appendMotionEvent(motionEventF);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3));
+        mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300));
+        mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301));
+        mClassifier.onTouchEvent(appendUpEvent(1, 19, 501));
         assertThat(mClassifier.isFalseTouch(), is(true));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
-        motionEventD.recycle();
-        motionEventE.recycle();
-        motionEventF.recycle();
     }
 
     @Test
     public void testPass_swipe() {
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, DPI * 3, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 1000, MotionEvent.ACTION_UP, 1, DPI * 3,
-                0);
 
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-
-        appendMotionEvent(motionEventB);
-        appendMotionEvent(motionEventC);
+        mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
+        mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
         assertThat(mClassifier.isFalseTouch(), is(false));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
-    }
-
-    private void appendMotionEvent(MotionEvent motionEvent) {
-        if (mMotionEvents.isEmpty()) {
-            when(mDataProvider.getFirstRecentMotionEvent()).thenReturn(motionEvent);
-        }
-
-        mMotionEvents.add(motionEvent);
-        when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
-
-        when(mDataProvider.getLastMotionEvent()).thenReturn(motionEvent);
-
-        mClassifier.onTouchEvent(motionEvent);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
index 1da4206..748c137 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -22,12 +22,12 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,24 +37,32 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class FalsingDataProviderTest extends SysuiTestCase {
+public class FalsingDataProviderTest extends ClassifierTest {
 
     private FalsingDataProvider mDataProvider;
 
     @Before
     public void setup() {
-        mDataProvider = new FalsingDataProvider(getContext());
+        super.setup();
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.xdpi = 100;
+        displayMetrics.ydpi = 100;
+        displayMetrics.widthPixels = 1000;
+        displayMetrics.heightPixels = 1000;
+        mDataProvider = new FalsingDataProvider(displayMetrics);
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        mDataProvider.onSessionEnd();
     }
 
     @Test
     public void test_trackMotionEvents() {
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 3, 6, 5);
-
-        mDataProvider.onMotionEvent(motionEventA);
-        mDataProvider.onMotionEvent(motionEventB);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendDownEvent(2, 9));
+        mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
+        mDataProvider.onMotionEvent(appendUpEvent(6, 5));
         List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
 
         assertThat(motionEventList.size(), is(3));
@@ -70,20 +78,12 @@
         assertThat(motionEventList.get(0).getY(), is(9f));
         assertThat(motionEventList.get(1).getY(), is(7f));
         assertThat(motionEventList.get(2).getY(), is(5f));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
     public void test_trackRecentMotionEvents() {
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 800, 4, 7);
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 1200, 6, 5);
-
-        mDataProvider.onMotionEvent(motionEventA);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1));
+        mDataProvider.onMotionEvent(appendMoveEvent(4, 7, 800));
         List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
 
         assertThat(motionEventList.size(), is(2));
@@ -96,7 +96,7 @@
         assertThat(motionEventList.get(0).getY(), is(9f));
         assertThat(motionEventList.get(1).getY(), is(7f));
 
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendUpEvent(6, 5, 1200));
 
         // Still two events, but event a is gone.
         assertThat(motionEventList.size(), is(2));
@@ -115,18 +115,14 @@
         assertThat(firstRealMotionEvent.getEventTime(), is(1L));
         assertThat(firstRealMotionEvent.getX(), is(2f));
         assertThat(firstRealMotionEvent.getY(), is(9f));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
     public void test_unpackMotionEvents() {
         // Batching only works for motion events of the same type.
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 1, 2, 9);
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 3, 6, 5);
+        MotionEvent motionEventA = appendMoveEvent(2, 9);
+        MotionEvent motionEventB = appendMoveEvent(4, 7);
+        MotionEvent motionEventC = appendMoveEvent(6, 5);
         motionEventA.addBatch(motionEventB);
         motionEventA.addBatch(motionEventC);
         // Note that calling addBatch changes properties on the original event, not just it's
@@ -148,114 +144,86 @@
         assertThat(motionEventList.get(0).getY(), is(9f));
         assertThat(motionEventList.get(1).getY(), is(7f));
         assertThat(motionEventList.get(2).getY(), is(5f));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
     public void test_getAngle() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
         assertThat((double) mDataProvider.getAngle(), closeTo(Math.PI / 4, .001));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -1, -1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(-1, -1));
         assertThat((double) mDataProvider.getAngle(), closeTo(5 * Math.PI / 4, .001));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 0);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(2, 0));
         assertThat((double) mDataProvider.getAngle(), closeTo(0, .001));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
     public void test_isHorizontal() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
         assertThat(mDataProvider.isHorizontal(), is(false));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(2, 1));
         assertThat(mDataProvider.isHorizontal(), is(true));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, -1));
         assertThat(mDataProvider.isHorizontal(), is(true));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
     public void test_isVertical() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 0);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 0));
         assertThat(mDataProvider.isVertical(), is(false));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(0, 1));
         assertThat(mDataProvider.isVertical(), is(true));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, -10));
         assertThat(mDataProvider.isVertical(), is(true));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
     public void test_isRight() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
         assertThat(mDataProvider.isRight(), is(true));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(0, 1));
         assertThat(mDataProvider.isRight(), is(false));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, -10));
         assertThat(mDataProvider.isRight(), is(false));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
@@ -263,31 +231,21 @@
     public void test_isUp() {
         // Remember that our y axis is flipped.
 
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, -1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, -1));
         assertThat(mDataProvider.isUp(), is(true));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 0);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(0, 0));
         assertThat(mDataProvider.isUp(), is(false));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, 10);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, 10));
         assertThat(mDataProvider.isUp(), is(false));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
-
-    private MotionEvent obtainMotionEvent(int action, long eventTimeMs, float x, float y) {
-        return MotionEvent.obtain(1, eventTimeMs, action, x, y, 0);
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
index cba9ee38..341b74b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
@@ -25,25 +25,27 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class PointerCountClassifierTest extends SysuiTestCase {
+public class PointerCountClassifierTest extends ClassifierTest {
 
-    @Mock
-    private FalsingDataProvider mDataProvider;
     private FalsingClassifier mClassifier;
 
     @Before
     public void setup() {
-        mClassifier = new PointerCountClassifier(mDataProvider);
+        super.setup();
+        mClassifier = new PointerCountClassifier(getDataProvider());
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
     }
 
     @Test
@@ -53,9 +55,7 @@
 
     @Test
     public void testPass_singlePointer() {
-        MotionEvent motionEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        mClassifier.onTouchEvent(motionEvent);
-        motionEvent.recycle();
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index 2ed7925..a6cabbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -31,8 +31,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,7 +44,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class ProximityClassifierTest extends SysuiTestCase {
+public class ProximityClassifierTest extends ClassifierTest {
 
     private static final long NS_PER_MS = 1000000;
 
@@ -57,12 +56,18 @@
 
     @Before
     public void setup() {
+        super.setup();
         MockitoAnnotations.initMocks(this);
         when(mDataProvider.getInteractionType()).thenReturn(GENERIC);
         when(mDistanceClassifier.isLongSwipe()).thenReturn(false);
         mClassifier = new ProximityClassifier(mDistanceClassifier, mDataProvider);
     }
 
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
     @Test
     public void testPass_uncovered() {
         touchDown();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
index 4bb3c15..0355dc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
@@ -34,8 +34,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,15 +43,15 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class TypeClassifierTest extends SysuiTestCase {
+public class TypeClassifierTest extends ClassifierTest {
 
     @Mock
     private FalsingDataProvider mDataProvider;
-
     private FalsingClassifier mClassifier;
 
     @Before
     public void setup() {
+        super.setup();
         MockitoAnnotations.initMocks(this);
         mClassifier = new TypeClassifier(mDataProvider);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
index 9d09a7e..25a1a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
@@ -18,128 +18,93 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Random;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class ZigZagClassifierTest extends SysuiTestCase {
+public class ZigZagClassifierTest extends ClassifierTest {
 
-    private static final long NS_PER_MS = 1000000;
-
-    @Mock
-    private FalsingDataProvider mDataProvider;
     private FalsingClassifier mClassifier;
-    private List<MotionEvent> mMotionEvents = new ArrayList<>();
-    private float mOffsetX = 0;
-    private float mOffsetY = 0;
-    private float mDx;
-    private float mDy;
 
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mDataProvider.getXdpi()).thenReturn(100f);
-        when(mDataProvider.getYdpi()).thenReturn(100f);
-        when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
-        mClassifier = new ZigZagClassifier(mDataProvider);
-
-
-        // Calculate the response to these calls on the fly, otherwise Mockito gets bogged down
-        // everytime we call appendMotionEvent.
-        when(mDataProvider.getFirstRecentMotionEvent()).thenAnswer(
-                (Answer<MotionEvent>) invocation -> mMotionEvents.get(0));
-        when(mDataProvider.getLastMotionEvent()).thenAnswer(
-                (Answer<MotionEvent>) invocation -> mMotionEvents.get(mMotionEvents.size() - 1));
-        when(mDataProvider.isHorizontal()).thenAnswer(
-                (Answer<Boolean>) invocation -> Math.abs(mDy) < Math.abs(mDx));
-        when(mDataProvider.isVertical()).thenAnswer(
-                (Answer<Boolean>) invocation -> Math.abs(mDy) > Math.abs(mDx));
-        when(mDataProvider.isRight()).thenAnswer((Answer<Boolean>) invocation -> mDx > 0);
-        when(mDataProvider.isUp()).thenAnswer((Answer<Boolean>) invocation -> mDy < 0);
+        super.setup();
+        mClassifier = new ZigZagClassifier(getDataProvider());
     }
 
     @After
     public void tearDown() {
-        clearMotionEvents();
+        super.tearDown();
     }
 
     @Test
     public void testPass_fewTouchesVertical() {
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(0, 0);
+        appendMoveEvent(0, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(0, 100);
+        appendMoveEvent(0, 100);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
     public void testPass_vertical() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(0, 100);
-        appendMotionEvent(0, 200);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(0, 100);
+        appendMoveEvent(0, 200);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
     public void testPass_fewTouchesHorizontal() {
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(0, 0);
+        appendMoveEvent(0, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(100, 0);
+        appendMoveEvent(100, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
     public void testPass_horizontal() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 0);
-        appendMotionEvent(200, 0);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 0);
+        appendMoveEvent(200, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
 
     @Test
     public void testFail_minimumTouchesVertical() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(0, 100);
-        appendMotionEvent(0, 1);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(0, 100);
+        appendMoveEvent(0, 1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void testFail_minimumTouchesHorizontal() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 0);
-        appendMotionEvent(1, 0);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 0);
+        appendMoveEvent(1, 0);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void testPass_fortyFiveDegreesStraight() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(10, 10);
-        appendMotionEvent(20, 20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(10, 10);
+        appendMoveEvent(20, 20);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
@@ -147,9 +112,9 @@
     public void testPass_horizontalZigZagVerticalStraight() {
         // This test looks just like testFail_horizontalZigZagVerticalStraight but with
         // a longer y range, making it look straighter.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(5, 100);
-        appendMotionEvent(-5, 200);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(5, 100);
+        appendMoveEvent(-5, 200);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
@@ -157,9 +122,9 @@
     public void testPass_horizontalStraightVerticalZigZag() {
         // This test looks just like testFail_horizontalStraightVerticalZigZag but with
         // a longer x range, making it look straighter.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 5);
-        appendMotionEvent(200, -5);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 5);
+        appendMoveEvent(200, -5);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
@@ -167,9 +132,9 @@
     public void testFail_horizontalZigZagVerticalStraight() {
         // This test looks just like testPass_horizontalZigZagVerticalStraight but with
         // a shorter y range, making it look more crooked.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(5, 10);
-        appendMotionEvent(-5, 20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(5, 10);
+        appendMoveEvent(-5, 20);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
@@ -177,217 +142,217 @@
     public void testFail_horizontalStraightVerticalZigZag() {
         // This test looks just like testPass_horizontalStraightVerticalZigZag but with
         // a shorter x range, making it look more crooked.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(10, 5);
-        appendMotionEvent(20, -5);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(10, 5);
+        appendMoveEvent(20, -5);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between0And45() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 5);
-        appendMotionEvent(200, 10);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 5);
+        appendMoveEvent(200, 10);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 0);
-        appendMotionEvent(200, 10);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 0);
+        appendMoveEvent(200, 10);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, -10);
-        appendMotionEvent(200, 10);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, -10);
+        appendMoveEvent(200, 10);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, -10);
-        appendMotionEvent(200, 50);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, -10);
+        appendMoveEvent(200, 50);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between45And90() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(10, 50);
-        appendMotionEvent(8, 100);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(10, 50);
+        appendMoveEvent(8, 100);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(1, 800);
-        appendMotionEvent(2, 900);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(1, 800);
+        appendMoveEvent(2, 900);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-10, 600);
-        appendMotionEvent(30, 700);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-10, 600);
+        appendMoveEvent(30, 700);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(40, 100);
-        appendMotionEvent(0, 101);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(40, 100);
+        appendMoveEvent(0, 101);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between90And135() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-10, 50);
-        appendMotionEvent(-24, 100);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-10, 50);
+        appendMoveEvent(-24, 100);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, 800);
-        appendMotionEvent(-20, 900);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, 800);
+        appendMoveEvent(-20, 900);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(30, 600);
-        appendMotionEvent(-10, 700);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(30, 600);
+        appendMoveEvent(-10, 700);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, 100);
-        appendMotionEvent(-10, 101);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, 100);
+        appendMoveEvent(-10, 101);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between135And180() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-120, 10);
-        appendMotionEvent(-200, 20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-120, 10);
+        appendMoveEvent(-200, 20);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, 8);
-        appendMotionEvent(-40, 2);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, 8);
+        appendMoveEvent(-40, 2);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-500, -2);
-        appendMotionEvent(-600, 70);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-500, -2);
+        appendMoveEvent(-600, 70);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, 100);
-        appendMotionEvent(-100, 1);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, 100);
+        appendMoveEvent(-100, 1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between180And225() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-120, -10);
-        appendMotionEvent(-200, -20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-120, -10);
+        appendMoveEvent(-200, -20);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, -8);
-        appendMotionEvent(-40, -2);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, -8);
+        appendMoveEvent(-40, -2);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-500, 2);
-        appendMotionEvent(-600, -70);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-500, 2);
+        appendMoveEvent(-600, -70);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, -100);
-        appendMotionEvent(-100, -1);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, -100);
+        appendMoveEvent(-100, -1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between225And270() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-12, -20);
-        appendMotionEvent(-20, -40);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-12, -20);
+        appendMoveEvent(-20, -40);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, -130);
-        appendMotionEvent(-40, -260);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, -130);
+        appendMoveEvent(-40, -260);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(1, -100);
-        appendMotionEvent(-6, -200);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(1, -100);
+        appendMoveEvent(-6, -200);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, -100);
-        appendMotionEvent(-10, -110);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, -100);
+        appendMoveEvent(-10, -110);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between270And315() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(12, -20);
-        appendMotionEvent(20, -40);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(12, -20);
+        appendMoveEvent(20, -40);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(20, -130);
-        appendMotionEvent(40, -260);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(20, -130);
+        appendMoveEvent(40, -260);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-1, -100);
-        appendMotionEvent(6, -200);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-1, -100);
+        appendMoveEvent(6, -200);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(80, -100);
-        appendMotionEvent(10, -110);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(80, -100);
+        appendMoveEvent(10, -110);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
     public void test_between315And360() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(120, -20);
-        appendMotionEvent(200, -40);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(120, -20);
+        appendMoveEvent(200, -40);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(200, -13);
-        appendMotionEvent(400, -30);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(200, -13);
+        appendMoveEvent(400, -30);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 10);
-        appendMotionEvent(600, -20);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 10);
+        appendMoveEvent(600, -20);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(80, -100);
-        appendMotionEvent(100, -1);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(80, -100);
+        appendMoveEvent(100, -1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
@@ -397,74 +362,50 @@
         // We use a pre-determined seed to make this test repeatable.
         Random rand = new Random(23);
         for (int i = 0; i < 100; i++) {
-            mOffsetX = rand.nextInt(2000) - 1000;
-            mOffsetY = rand.nextInt(2000) - 1000;
+            setOffsetX(rand.nextInt(2000) - 1000);
+            setOffsetY(rand.nextInt(2000) - 1000);
             try {
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_fewTouchesVertical();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_vertical();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_horizontalStraightVerticalZigZag();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_horizontalZigZagVerticalStraight();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_minimumTouchesHorizontal();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_minimumTouchesVertical();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_fewTouchesHorizontal();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_fortyFiveDegreesStraight();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_horizontal();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_horizontalStraightVerticalZigZag();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_horizontalZigZagVerticalStraight();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between0And45();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between45And90();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between90And135();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between135And180();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between180And225();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between225And270();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between270And315();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between315And360();
             } catch (AssertionError e) {
                 throw new AssertionError("Random origin failure in iteration " + i, e);
             }
         }
     }
-
-    private void clearMotionEvents() {
-        for (MotionEvent motionEvent : mMotionEvents) {
-            motionEvent.recycle();
-        }
-        mMotionEvents.clear();
-    }
-
-    private void appendMotionEvent(float x, float y) {
-        x += mOffsetX;
-        y += mOffsetY;
-
-        long eventTime = mMotionEvents.size() + 1;
-        MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, MotionEvent.ACTION_DOWN, x, y,
-                0);
-        mMotionEvents.add(motionEvent);
-
-        mDx = mDataProvider.getFirstRecentMotionEvent().getX()
-                - mDataProvider.getLastMotionEvent().getX();
-        mDy = mDataProvider.getFirstRecentMotionEvent().getY()
-                - mDataProvider.getLastMotionEvent().getY();
-
-        mClassifier.onTouchEvent(motionEvent);
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
deleted file mode 100644
index 6302f9d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 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.privacy
-
-import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.SysuiTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class PrivacyDialogBuilderTest : SysuiTestCase() {
-
-    companion object {
-        val TEST_UID = 1
-    }
-
-    @Test
-    fun testGenerateAppsList() {
-        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Bar", TEST_UID, context))
-        val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
-                "Bar", TEST_UID, context))
-        val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
-                "Foo", TEST_UID, context))
-        val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Baz", TEST_UID, context))
-
-        val items = listOf(bar2, foo0, baz1, bar3)
-
-        val textBuilder = PrivacyDialogBuilder(context, items)
-
-        val list = textBuilder.appsAndTypes
-        assertEquals(3, list.size)
-        val appsList = list.map { it.first }
-        val typesList = list.map { it.second }
-        // List is sorted by number of types and then by types
-        assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
-        assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
-        assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
-        assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
-    }
-
-    @Test
-    fun testOrder() {
-        // We want location to always go last, so it will go in the "+ other apps"
-        val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
-                PrivacyApplication("Camera", TEST_UID, context))
-        val appMicrophone =
-                PrivacyItem(PrivacyType.TYPE_MICROPHONE,
-                        PrivacyApplication("Microphone", TEST_UID, context))
-        val appLocation =
-                PrivacyItem(PrivacyType.TYPE_LOCATION,
-                        PrivacyApplication("Location", TEST_UID, context))
-
-        val items = listOf(appLocation, appMicrophone, appCamera)
-        val textBuilder = PrivacyDialogBuilder(context, items)
-        val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
-        assertEquals(listOf("Camera", "Microphone", "Location"), appList)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
deleted file mode 100644
index e2e0bb1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2018 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.privacy
-
-import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.Context
-import android.content.Intent
-import android.content.pm.UserInfo
-import android.os.Handler
-import android.os.UserHandle
-import android.os.UserManager
-import android.provider.DeviceConfig
-import android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dependency
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
-import org.hamcrest.Matchers.hasItem
-import org.hamcrest.Matchers.not
-import org.hamcrest.Matchers.nullValue
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThat
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyList
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-@RunWithLooper
-class PrivacyItemControllerTest : SysuiTestCase() {
-
-    companion object {
-        val CURRENT_USER_ID = ActivityManager.getCurrentUser()
-        val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
-        const val SYSTEM_UID = 1000
-        const val TEST_PACKAGE_NAME = "test"
-        const val DEVICE_SERVICES_STRING = "Device services"
-        const val TAG = "PrivacyItemControllerTest"
-        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
-    }
-
-    @Mock
-    private lateinit var appOpsController: AppOpsController
-    @Mock
-    private lateinit var callback: PrivacyItemController.Callback
-    @Mock
-    private lateinit var userManager: UserManager
-    @Captor
-    private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
-    @Captor
-    private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
-
-    private lateinit var testableLooper: TestableLooper
-    private lateinit var privacyItemController: PrivacyItemController
-    private lateinit var handler: Handler
-
-    fun PrivacyItemController(context: Context) =
-            PrivacyItemController(context, appOpsController, handler, handler)
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        testableLooper = TestableLooper.get(this)
-        handler = Handler(testableLooper.looper)
-
-        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-        mDependency.injectTestDependency(Dependency.BG_HANDLER, handler)
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
-        mContext.addMockSystemService(UserManager::class.java, userManager)
-        mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
-                DEVICE_SERVICES_STRING)
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "true", false)
-
-        doReturn(listOf(object : UserInfo() {
-            init {
-                id = CURRENT_USER_ID
-            }
-        })).`when`(userManager).getProfiles(anyInt())
-
-        privacyItemController = PrivacyItemController(mContext)
-    }
-
-    @After
-    fun tearDown() {
-        DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_PRIVACY)
-    }
-
-    @Test
-    fun testSetListeningTrueByAddingCallback() {
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback::class.java))
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(anyList())
-    }
-
-    @Test
-    fun testSetListeningFalseByRemovingLastCallback() {
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController, never()).removeCallback(any(IntArray::class.java),
-                any(AppOpsController.Callback::class.java))
-        privacyItemController.removeCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback::class.java))
-        verify(callback).privacyChanged(emptyList())
-    }
-
-    @Test
-    fun testDistinctItems() {
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
-                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(capture(argCaptor))
-        assertEquals(1, argCaptor.value.size)
-    }
-
-    @Test
-    fun testSystemApps() {
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
-                0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(capture(argCaptor))
-        assertEquals(1, argCaptor.value.size)
-        assertEquals(context.getString(R.string.device_services),
-                argCaptor.value[0].application.applicationName)
-    }
-
-    @Test
-    fun testRegisterReceiver_allUsers() {
-        val spiedContext = spy(mContext)
-        val itemController = PrivacyItemController(spiedContext)
-        itemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
-                eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
-                eq(null))
-        verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
-    }
-
-    @Test
-    fun testReceiver_ACTION_USER_FOREGROUND() {
-        privacyItemController.userSwitcherReceiver.onReceive(context,
-                Intent(Intent.ACTION_USER_FOREGROUND))
-        verify(userManager).getProfiles(anyInt())
-    }
-
-    @Test
-    fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
-        privacyItemController.userSwitcherReceiver.onReceive(context,
-                Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
-        verify(userManager).getProfiles(anyInt())
-    }
-
-    @Test
-    fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
-        privacyItemController.userSwitcherReceiver.onReceive(context,
-                Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
-        verify(userManager).getProfiles(anyInt())
-    }
-
-    @Test
-    fun testAddMultipleCallbacks() {
-        val otherCallback = mock(PrivacyItemController.Callback::class.java)
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(anyList())
-
-        privacyItemController.addCallback(otherCallback)
-        testableLooper.processAllMessages()
-        verify(otherCallback).privacyChanged(anyList())
-        // Adding a callback should not unnecessarily call previous ones
-        verifyNoMoreInteractions(callback)
-    }
-
-    @Test
-    fun testMultipleCallbacksAreUpdated() {
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-
-        val otherCallback = mock(PrivacyItemController.Callback::class.java)
-        privacyItemController.addCallback(callback)
-        privacyItemController.addCallback(otherCallback)
-        testableLooper.processAllMessages()
-        reset(callback)
-        reset(otherCallback)
-
-        verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
-        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(anyList())
-        verify(otherCallback).privacyChanged(anyList())
-    }
-
-    @Test
-    fun testRemoveCallback() {
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-        val otherCallback = mock(PrivacyItemController.Callback::class.java)
-        privacyItemController.addCallback(callback)
-        privacyItemController.addCallback(otherCallback)
-        testableLooper.processAllMessages()
-        reset(callback)
-        reset(otherCallback)
-
-        verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
-        privacyItemController.removeCallback(callback)
-        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
-        testableLooper.processAllMessages()
-        verify(callback, never()).privacyChanged(anyList())
-        verify(otherCallback).privacyChanged(anyList())
-    }
-
-    @Test
-    fun testListShouldNotHaveNull() {
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
-                        AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-
-        verify(callback).privacyChanged(capture(argCaptor))
-        assertEquals(1, argCaptor.value.size)
-        assertThat(argCaptor.value, not(hasItem(nullValue())))
-    }
-
-    @Test
-    fun testListShouldBeCopy() {
-        val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
-                PrivacyApplication("", TEST_UID, mContext)))
-        privacyItemController.privacyList = list
-        val privacyList = privacyItemController.privacyList
-        assertEquals(list, privacyList)
-        assertTrue(list !== privacyList)
-    }
-
-    @Test
-    fun testNotListeningWhenIndicatorsDisabled() {
-        privacyItemController.devicePropertyChangedListener.onPropertyChanged(
-                DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "false")
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback::class.java))
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index de8dcfe..7063ddf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -78,7 +79,8 @@
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         StatusBarStateController stateController = mock(StatusBarStateController.class);
         mGroupManager = new NotificationGroupManager(stateController);
-        mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController);
+        mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController,
+                mock(KeyguardBypassController.class));
         mHeadsUpManager.setUp(null, mGroupManager, null, null);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 7e6335d..524ad85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -57,11 +58,13 @@
     private ExpandableNotificationRow mSecond;
     @Mock
     private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private KeyguardBypassController mBypassController;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mRoundnessManager = new NotificationRoundnessManager();
+        mRoundnessManager = new NotificationRoundnessManager(mBypassController);
         com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
         mFirst = testHelper.createRow();
@@ -260,15 +263,15 @@
     }
 
     @Test
-    public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+    public void testTrackingHeadsUpPartiallyRoundedIfPushingDown() {
         mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
         mRoundnessManager.setTrackingHeadsUp(mFirst);
         mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
                 createSection(mSecond, mSecond),
                 createSection(null, null)
         });
-        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(0.5f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.5f, mFirst.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 66255dd..92173cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -200,17 +200,6 @@
     }
 
     @Test
-    public void testAntiBurnInOffset() {
-        final int burnInOffset = 30;
-        mStackScroller.setAntiBurnInOffsetX(burnInOffset);
-        mStackScroller.setHideAmount(0.0f, 0.0f);
-        Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
-        mStackScroller.setHideAmount(1.0f, 1.0f);
-        Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
-                0.01 /* delta */);
-    }
-
-    @Test
     public void updateEmptyView_dndSuppressing() {
         when(mEmptyShadeView.willBeGone()).thenReturn(true);
         when(mBar.areNotificationsHidden()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 0479b4a..b45707e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -32,6 +32,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -56,6 +57,8 @@
     private HeadsUpStatusBarView mHeadsUpStatusBarView;
     private HeadsUpManagerPhone mHeadsUpManager;
     private View mOperatorNameView;
+    private StatusBarStateController mStatusbarStateController;
+    private KeyguardBypassController mBypassController;
 
     @Before
     public void setUp() throws Exception {
@@ -67,16 +70,20 @@
                 mock(TextView.class));
         mHeadsUpManager = mock(HeadsUpManagerPhone.class);
         mOperatorNameView = new View(mContext);
+        mStatusbarStateController = mock(StatusBarStateController.class);
+        mBypassController = mock(KeyguardBypassController.class);
         mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                 mock(NotificationIconAreaController.class),
                 mHeadsUpManager,
+                mStatusbarStateController,
+                mBypassController,
                 mHeadsUpStatusBarView,
                 mStackScroller,
                 mPanelView,
                 new View(mContext),
                 mOperatorNameView,
                 new View(mContext));
-        mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
+        mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
     }
 
     @Test
@@ -139,11 +146,13 @@
 
     @Test
     public void testHeaderReadFromOldController() {
-        mHeadsUpAppearanceController.setExpandedHeight(1.0f, 1.0f);
+        mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
 
         HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
                 mock(NotificationIconAreaController.class),
                 mHeadsUpManager,
+                mStatusbarStateController,
+                mBypassController,
                 mHeadsUpStatusBarView,
                 mStackScroller,
                 mPanelView,
@@ -154,8 +163,8 @@
 
         Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
                 newController.mExpandedHeight, 0.0f);
-        Assert.assertEquals(mHeadsUpAppearanceController.mExpandFraction,
-                newController.mExpandFraction, 0.0f);
+        Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
+                newController.mAppearFraction, 0.0f);
         Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
                 newController.mIsExpanded);
     }
@@ -172,7 +181,7 @@
         verify(mPanelView).removeVerticalTranslationListener(any());
         verify(mPanelView).removeTrackingHeadsUpListener(any());
         verify(mPanelView).setHeadsUpAppearanceController(any());
-        verify(mStackScroller).removeOnExpandedHeightListener(any());
+        verify(mStackScroller).removeOnExpandedHeightChangedListener(any());
         verify(mStackScroller).removeOnLayoutChangeListener(any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index a66345b..f8b9e68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -57,14 +57,16 @@
     @Mock private VisualStabilityManager mVSManager;
     @Mock private StatusBar mBar;
     @Mock private StatusBarStateController mStatusBarStateController;
+    @Mock private KeyguardBypassController mBypassController;
     private boolean mLivesPastNormalTime;
 
     private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
         TestableHeadsUpManagerPhone(Context context, View statusBarWindowView,
                 NotificationGroupManager groupManager, StatusBar bar,
                 VisualStabilityManager vsManager,
-                StatusBarStateController statusBarStateController) {
-            super(context, statusBarStateController);
+                StatusBarStateController statusBarStateController,
+                KeyguardBypassController keyguardBypassController) {
+            super(context, statusBarStateController, keyguardBypassController);
             setUp(statusBarWindowView, groupManager, bar, vsManager);
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -84,7 +86,7 @@
                 .thenReturn(TEST_AUTO_DISMISS_TIME);
         when(mVSManager.isReorderingAllowed()).thenReturn(true);
         mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mStatusBarWindowView,
-                mGroupManager, mBar, mVSManager, mStatusBarStateController);
+                mGroupManager, mBar, mVSManager, mStatusBarStateController, mBypassController);
         super.setUp();
         mHeadsUpManager.mHandler = mTestHandler;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 4b5e1dd..d14b460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -120,7 +121,7 @@
                         new StatusBarStateControllerImpl(),
                         bypassController);
         PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
-                bypassController);
+                bypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class));
         mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
                 bypassController);
         mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 66eb299..3357be8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -42,7 +42,7 @@
     @Before
     public void setUp() {
         mInner = WakeLock.createPartialInner(mContext, WakeLockTest.class.getName());
-        mWakeLock = WakeLock.wrap(mInner);
+        mWakeLock = WakeLock.wrap(mInner, 20000);
     }
 
     @After
@@ -70,14 +70,6 @@
     }
 
     @Test
-    public void wakeLock_refCounted() {
-        mWakeLock.acquire(WHY);
-        mWakeLock.acquire(WHY);
-        mWakeLock.release(WHY);
-        assertTrue(mInner.isHeld());
-    }
-
-    @Test
     public void wakeLock_wrap() {
         boolean[] ran = new boolean[1];
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 84482ec..e5fddef 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2617,9 +2617,11 @@
                     final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
                     final boolean wasValidated = nai.lastValidated;
                     final boolean wasDefault = isDefaultNetwork(nai);
-                    if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified
-                            && valid) {
-                        nai.captivePortalLoginNotified = true;
+                    // Only show a connected notification if the network is pending validation
+                    // after the captive portal app was open, and it has now validated.
+                    if (nai.captivePortalValidationPending && valid) {
+                        // User is now logged in, network validated.
+                        nai.captivePortalValidationPending = false;
                         showNetworkNotification(nai, NotificationType.LOGGED_IN);
                     }
 
@@ -2690,9 +2692,6 @@
                         final int oldScore = nai.getCurrentScore();
                         nai.lastCaptivePortalDetected = visible;
                         nai.everCaptivePortalDetected |= visible;
-                        if (visible) {
-                            nai.captivePortalLoginNotified = false;
-                        }
                         if (nai.lastCaptivePortalDetected &&
                             Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
                             if (DBG) log("Avoiding captive portal network: " + nai.name());
@@ -3501,6 +3500,12 @@
                 new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
+        // This runs on a random binder thread, but getNetworkAgentInfoForNetwork is thread-safe,
+        // and captivePortalValidationPending is volatile.
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai != null) {
+            nai.captivePortalValidationPending = true;
+        }
         Binder.withCleanCallingIdentity(() ->
                 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
     }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 1a6faec..e66e596 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1255,23 +1255,21 @@
         }
     }
 
-    public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
-        notifyPhysicalChannelConfigurationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                configs);
-    }
-
-    public void notifyPhysicalChannelConfigurationForSubscriber(int subId,
+    /**
+     * Notify physical channel configuration according to subscripton ID and phone ID
+     */
+    public void notifyPhysicalChannelConfigurationForSubscriber(int phoneId, int subId,
             List<PhysicalChannelConfig> configs) {
         if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
             return;
         }
 
         if (VDBG) {
-            log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
+            log("notifyPhysicalChannelConfiguration: subId=" + subId + " phoneId=" + phoneId
+                    + " configs=" + configs);
         }
 
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mPhysicalChannelConfigs.set(phoneId, configs);
                 for (Record r : mRecords) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 6eb9f0c..0748279 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
@@ -193,7 +194,7 @@
         // with other system events, any duration calculations should be done use startTime so as
         // not to be affected by discontinuities created by RTC adjustments.
         public final long startTimeDebug;
-        public final int usageHint;
+        public final AudioAttributes attrs;
         public final int uid;
         public final String opPkg;
         public final String reason;
@@ -206,12 +207,12 @@
         public VibrationEffect originalEffect;
 
         private Vibration(IBinder token, VibrationEffect effect,
-                int usageHint, int uid, String opPkg, String reason) {
+                AudioAttributes attrs, int uid, String opPkg, String reason) {
             this.token = token;
             this.effect = effect;
             this.startTime = SystemClock.elapsedRealtime();
             this.startTimeDebug = System.currentTimeMillis();
-            this.usageHint = usageHint;
+            this.attrs = attrs;
             this.uid = uid;
             this.opPkg = opPkg;
             this.reason = reason;
@@ -231,7 +232,7 @@
         }
 
         public boolean isHapticFeedback() {
-            if (VibratorService.this.isHapticFeedback(usageHint)) {
+            if (VibratorService.this.isHapticFeedback(attrs.getUsage())) {
                 return true;
             }
             if (effect instanceof VibrationEffect.Prebaked) {
@@ -256,15 +257,15 @@
         }
 
         public boolean isNotification() {
-            return VibratorService.this.isNotification(usageHint);
+            return VibratorService.this.isNotification(attrs.getUsage());
         }
 
         public boolean isRingtone() {
-            return VibratorService.this.isRingtone(usageHint);
+            return VibratorService.this.isRingtone(attrs.getUsage());
         }
 
         public boolean isAlarm() {
-            return VibratorService.this.isAlarm(usageHint);
+            return VibratorService.this.isAlarm(attrs.getUsage());
         }
 
         public boolean isFromSystem() {
@@ -273,7 +274,7 @@
 
         public VibrationInfo toInfo() {
             return new VibrationInfo(
-                    startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
+                    startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
         }
     }
 
@@ -281,18 +282,18 @@
         private final long mStartTimeDebug;
         private final VibrationEffect mEffect;
         private final VibrationEffect mOriginalEffect;
-        private final int mUsageHint;
+        private final AudioAttributes mAttrs;
         private final int mUid;
         private final String mOpPkg;
         private final String mReason;
 
         public VibrationInfo(long startTimeDebug, VibrationEffect effect,
-                VibrationEffect originalEffect, int usageHint, int uid,
+                VibrationEffect originalEffect, AudioAttributes attrs, int uid,
                 String opPkg, String reason) {
             mStartTimeDebug = startTimeDebug;
             mEffect = effect;
             mOriginalEffect = originalEffect;
-            mUsageHint = usageHint;
+            mAttrs = attrs;
             mUid = uid;
             mOpPkg = opPkg;
             mReason = reason;
@@ -307,8 +308,8 @@
                     .append(mEffect)
                     .append(", originalEffect: ")
                     .append(mOriginalEffect)
-                    .append(", usageHint: ")
-                    .append(mUsageHint)
+                    .append(", attrs: ")
+                    .append(mAttrs)
                     .append(", uid: ")
                     .append(mUid)
                     .append(", opPkg: ")
@@ -549,12 +550,11 @@
     }
 
     @Override // Binder call
-    public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
-            IBinder token) {
+    public void vibrate(int uid, String opPkg, VibrationEffect effect,
+            @Nullable AudioAttributes attrs, String reason, IBinder token) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
         try {
-            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
-                    != PackageManager.PERMISSION_GRANTED) {
+            if (!hasPermission(android.Manifest.permission.VIBRATE)) {
                 throw new SecurityException("Requires VIBRATE permission");
             }
             if (token == null) {
@@ -566,6 +566,22 @@
                 return;
             }
 
+            if (attrs == null) {
+                attrs = new AudioAttributes.Builder()
+                        .setUsage(AudioAttributes.USAGE_UNKNOWN)
+                        .build();
+            }
+
+            if (shouldBypassDnd(attrs)) {
+                if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                        || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+                        || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
+                    final int flags = attrs.getAllFlags()
+                            & ~AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+                    attrs = new AudioAttributes.Builder(attrs).replaceFlags(flags).build();
+                }
+            }
+
             // If our current vibration is longer than the new vibration and is the same amplitude,
             // then just let the current one finish.
             synchronized (mLock) {
@@ -608,13 +624,13 @@
                     return;
                 }
 
-                Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
+                Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
                 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                         > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
                         && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
                     Slog.e(TAG, "Ignoring incoming vibration as process with"
-                            + " uid = " + uid + " is background,"
-                            + " usage = " + AudioAttributes.usageToString(vib.usageHint));
+                            + " uid= " + uid + " is background,"
+                            + " attrs= " + vib.attrs);
                     return;
                 }
                 linkVibration(vib);
@@ -632,6 +648,11 @@
         }
     }
 
+    private boolean hasPermission(String permission) {
+        return mContext.checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     private static boolean isRepeatingVibration(VibrationEffect effect) {
         return effect.getDuration() == Long.MAX_VALUE;
     }
@@ -760,14 +781,14 @@
             if (vib.effect instanceof VibrationEffect.OneShot) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
-                doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
+                doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.attrs);
                 mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
             } else if (vib.effect instanceof VibrationEffect.Waveform) {
                 // mThread better be null here. doCancelVibrate should always be
                 // called before startNextVibrationLocked or startVibrationLocked.
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
-                mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
+                mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
                 mThread.start();
             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -788,13 +809,14 @@
             return true;
         }
 
-        if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+        if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
             return true;
         }
 
-        if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
-                vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
-                vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
+        if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM
+                || vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
+                || vib.attrs.getUsage()
+                    == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
             return true;
         }
 
@@ -887,12 +909,24 @@
         }
     }
 
+    private static boolean shouldBypassDnd(AudioAttributes attrs) {
+        return (attrs.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
+    }
+
     private int getAppOpMode(Vibration vib) {
         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
-                vib.usageHint, vib.uid, vib.opPkg);
+                vib.attrs.getUsage(), vib.uid, vib.opPkg);
         if (mode == AppOpsManager.MODE_ALLOWED) {
             mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
         }
+
+        if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(vib.attrs)) {
+            // If we're just ignoring the vibration op then this is set by DND and we should ignore
+            // if we're asked to bypass. AppOps won't be able to record this operation, so make
+            // sure we at least note it in the logs for debugging.
+            Slog.d(TAG, "Bypassing DND for vibration: " + vib);
+            mode = AppOpsManager.MODE_ALLOWED;
+        }
         return mode;
     }
 
@@ -1032,7 +1066,7 @@
         return vibratorExists();
     }
 
-    private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
+    private void doVibratorOn(long millis, int amplitude, int uid, AudioAttributes attrs) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
         try {
             synchronized (mInputDeviceVibrators) {
@@ -1046,10 +1080,8 @@
                 noteVibratorOnLocked(uid, millis);
                 final int vibratorCount = mInputDeviceVibrators.size();
                 if (vibratorCount != 0) {
-                    final AudioAttributes attributes =
-                            new AudioAttributes.Builder().setUsage(usageHint).build();
                     for (int i = 0; i < vibratorCount; i++) {
-                        mInputDeviceVibrators.get(i).vibrate(millis, attributes);
+                        mInputDeviceVibrators.get(i).vibrate(millis, attrs);
                     }
                 } else {
                     // Note: ordering is important here! Many haptic drivers will reset their
@@ -1118,7 +1150,7 @@
                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
                 return 0;
             }
-            Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
+            Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
                     vib.opPkg, vib.reason + " (fallback)");
             final int intensity = getCurrentIntensityLocked(fallbackVib);
             linkVibration(fallbackVib);
@@ -1213,14 +1245,14 @@
     private class VibrateThread extends Thread {
         private final VibrationEffect.Waveform mWaveform;
         private final int mUid;
-        private final int mUsageHint;
+        private final AudioAttributes mAttrs;
 
         private boolean mForceStop;
 
-        VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
+        VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) {
             mWaveform = waveform;
             mUid = uid;
-            mUsageHint = usageHint;
+            mAttrs = attrs;
             mTmpWorkSource.set(uid);
             mWakeLock.setWorkSource(mTmpWorkSource);
         }
@@ -1295,7 +1327,7 @@
                                     // appropriate intervals.
                                     onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
                                             repeat);
-                                    doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
+                                    doVibratorOn(onDuration, amplitude, mUid, mAttrs);
                                 } else {
                                     doVibratorSetAmplitude(amplitude);
                                 }
@@ -1612,8 +1644,9 @@
 
                 VibrationEffect effect =
                         VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
-                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
-                        "Shell Command", mToken);
+                AudioAttributes attrs = createAudioAttributes(commonOptions);
+                vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
+                        mToken);
                 return 0;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -1672,8 +1705,9 @@
                             amplitudesList.stream().mapToInt(Integer::intValue).toArray();
                     effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
                 }
-                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
-                        "Shell Command", mToken);
+                AudioAttributes attrs = createAudioAttributes(commonOptions);
+                vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
+                        mToken);
                 return 0;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -1703,14 +1737,25 @@
 
                 VibrationEffect effect =
                         VibrationEffect.get(id, false);
-                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
-                        "Shell Command", mToken);
+                AudioAttributes attrs = createAudioAttributes(commonOptions);
+                vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
+                        mToken);
                 return 0;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
             }
         }
 
+        private AudioAttributes createAudioAttributes(CommonOptions commonOptions) {
+            final int flags = commonOptions.force
+                    ? AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
+                    : 0;
+            return new AudioAttributes.Builder()
+                    .setUsage(AudioAttributes.USAGE_UNKNOWN)
+                    .setFlags(flags)
+                    .build();
+        }
+
         @Override
         public void onHelp() {
             try (PrintWriter pw = getOutPrintWriter();) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 7ab70fa..ed64475 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -42,7 +42,7 @@
     static final boolean DEBUG_ALL = false;
 
     // Available log categories in the activity manager package.
-    static final boolean DEBUG_ANR = true;  // STOPSHIP disable it (b/113252928)
+    static final boolean DEBUG_ANR = false;
     static final boolean DEBUG_BACKGROUND_CHECK = DEBUG_ALL || false;
     static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
     static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b342d37..91ae156 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.provider.DeviceConfig.Properties;
 import android.server.ServerProtoEnums;
 import android.sysprop.VoldProperties;
 import android.text.TextUtils;
@@ -356,6 +357,7 @@
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.PriorityDump;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerService;
@@ -392,6 +394,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
 
@@ -861,6 +864,51 @@
      */
     final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
 
+    /**
+     * Depth of overlapping activity-start PSS deferral notes
+     */
+    private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
+
+    private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
+            new ActivityMetricsLaunchObserver() {
+        @Override
+        public void onActivityLaunched(byte[] activity, int temperature) {
+            // This is safe to force to the head of the queue because it relies only
+            // on refcounting to track begin/end of deferrals, not on actual
+            // message ordering.  We don't care *what* activity is being
+            // launched; only that we're doing so.
+            if (mPssDeferralTime > 0) {
+                final Message msg = mBgHandler.obtainMessage(DEFER_PSS_MSG);
+                mBgHandler.sendMessageAtFrontOfQueue(msg);
+            }
+        }
+
+        // The other observer methods are unused
+        @Override
+        public void onIntentStarted(Intent intent) {
+        }
+
+        @Override
+        public void onIntentFailed() {
+        }
+
+        @Override
+        public void onActivityLaunchCancelled(byte[] abortingActivity) {
+        }
+
+        @Override
+        public void onActivityLaunchFinished(byte[] finalActivity) {
+        }
+    };
+
+    /**
+     * How long we defer PSS gathering while activities are starting, in milliseconds.
+     * This is adjustable via DeviceConfig.  If it is zero or negative, no PSS deferral
+     * is done.
+     */
+    private volatile long mPssDeferralTime = 0;
+    private static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
+
     private boolean mBinderTransactionTrackingEnabled = false;
 
     /**
@@ -874,6 +922,20 @@
      */
     boolean mFullPssPending = false;
 
+    /**
+     * Observe DeviceConfig changes to the PSS calculation interval
+     */
+    private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+                @Override
+                public void onPropertiesChanged(Properties properties) {
+                    mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "Activity-start PSS delay now "
+                                + mPssDeferralTime + " ms");
+                    }
+                }
+            };
 
     /**
      * This is for verifying the UID report flow.
@@ -1838,6 +1900,8 @@
     }
 
     static final int COLLECT_PSS_BG_MSG = 1;
+    static final int DEFER_PSS_MSG = 2;
+    static final int STOP_DEFERRING_PSS_MSG = 3;
 
     final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
         @Override
@@ -1945,6 +2009,30 @@
                     }
                 } while (true);
             }
+
+            case DEFER_PSS_MSG: {
+                deferPssForActivityStart();
+            } break;
+
+            case STOP_DEFERRING_PSS_MSG: {
+                final int nesting = mActivityStartingNesting.decrementAndGet();
+                if (nesting <= 0) {
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
+                                + nesting);
+                    }
+                    if (nesting < 0) {
+                        Slog.wtf(TAG, "Activity start nesting undercount!");
+                        mActivityStartingNesting.incrementAndGet();
+                    }
+                } else {
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
+                    }
+                }
+            }
+            break;
+
             }
         }
     };
@@ -3694,9 +3782,7 @@
             ArrayList<Integer> nativePids) {
         ArrayList<Integer> extraPids = null;
 
-        if (DEBUG_ANR) {
-            Slog.d(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
-        }
+        Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
 
         // Measure CPU usage as soon as we're called in order to get a realistic sampling
         // of the top users at the time of the request.
@@ -3718,8 +3804,8 @@
                     if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
 
                     extraPids.add(stats.pid);
-                } else if (DEBUG_ANR) {
-                    Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
+                } else {
+                    Slog.i(TAG, "Skipping next CPU consuming process, not a java proc: "
                             + stats.pid);
                 }
             }
@@ -3737,9 +3823,6 @@
         if (tracesFile == null) {
             return null;
         }
-        if (DEBUG_ANR) {
-            Slog.d(TAG, "Dumping to " + tracesFile.getAbsolutePath());
-        }
 
         dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
         return tracesFile;
@@ -3832,6 +3915,8 @@
     public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
             ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
 
+        Slog.i(TAG, "Dumping to " + tracesFile);
+
         // We don't need any sort of inotify based monitoring when we're dumping traces via
         // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
         // control of all writes to the file in question.
@@ -3843,7 +3928,7 @@
         if (firstPids != null) {
             int num = firstPids.size();
             for (int i = 0; i < num; i++) {
-                if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid " + firstPids.get(i));
+                Slog.i(TAG, "Collecting stacks for pid " + firstPids.get(i));
                 final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
                                                                 remainingTime);
 
@@ -3863,7 +3948,7 @@
         // Next collect the stacks of the native pids
         if (nativePids != null) {
             for (int pid : nativePids) {
-                if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
+                Slog.i(TAG, "Collecting stacks for native pid " + pid);
                 final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
 
                 final long start = SystemClock.elapsedRealtime();
@@ -3887,7 +3972,7 @@
         // Lastly, dump stacks for all extra PIDs from the CPU tracker.
         if (extraPids != null) {
             for (int pid : extraPids) {
-                if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
+                Slog.i(TAG, "Collecting stacks for extra pid " + pid);
 
                 final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
 
@@ -3903,6 +3988,7 @@
                 }
             }
         }
+        Slog.i(TAG, "Done dumping");
     }
 
     @Override
@@ -8845,6 +8931,12 @@
                 NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
         mHiddenApiBlacklist.registerObserver();
 
+        final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ActivityThread.currentApplication().getMainExecutor(),
+                mPssDelayConfigListener);
+
         synchronized (this) {
             mDebugApp = mOrigDebugApp = debugApp;
             mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
@@ -8861,6 +8953,7 @@
                     com.android.internal.R.bool.config_multiuserDelayUserDataLocking);
 
             mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
+            mPssDeferralTime = pssDeferralMs;
         }
     }
 
@@ -8923,6 +9016,7 @@
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis());
 
         mAtmInternal.updateTopComponentForFactoryTest();
+        mAtmInternal.getLaunchObserverRegistry().registerLaunchObserver(mActivityLaunchObserver);
 
         watchDeviceProvisioning(mContext);
 
@@ -16146,7 +16240,13 @@
             return false;
         }
         if (mPendingPssProcesses.size() == 0) {
-            mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+            final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
+                    ? mPssDeferralTime : 0;
+            if (DEBUG_PSS && deferral > 0) {
+                Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+                        + deferral + " ms");
+            }
+            mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, deferral);
         }
         if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
         proc.pssProcState = procState;
@@ -16156,6 +16256,30 @@
     }
 
     /**
+     * Re-defer a posted PSS collection pass, if one exists.  Assumes deferral is
+     * currently active policy when called.
+     */
+    private void deferPssIfNeededLocked() {
+        if (mPendingPssProcesses.size() > 0) {
+            mBgHandler.removeMessages(COLLECT_PSS_BG_MSG);
+            mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, mPssDeferralTime);
+        }
+    }
+
+    private void deferPssForActivityStart() {
+        synchronized (ActivityManagerService.this) {
+            if (mPssDeferralTime > 0) {
+                if (DEBUG_PSS) {
+                    Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
+                }
+                deferPssIfNeededLocked();
+                mActivityStartingNesting.getAndIncrement();
+                mBgHandler.sendEmptyMessageDelayed(STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
+            }
+        }
+    }
+
+    /**
      * Schedule PSS collection of all processes.
      */
     void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5ade50a..bcfe71b8 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -431,7 +431,7 @@
             for (int i = 0; i < N; i++) {
                 ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+                    if (computeOomAdjLocked(app, app.getCurRawAdj(), TOP_APP, true, now,
                             true)) {
                         retryCycles = true;
                     }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 90edcb5..def7f75 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -54,7 +54,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -503,8 +502,6 @@
             try {
                 if (args.length > 1 && "--hal".equals(args[0])) {
                     dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
-                } else if (args.length > 0 && "--proto".equals(args[0])) {
-                    dumpProto(fd);
                 } else {
                     dumpInternal(pw);
                 }
@@ -1296,49 +1293,6 @@
         mUsageStats.print(pw);
     }
 
-    private void dumpProto(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
-        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
-            final int userId = user.getUserHandle().getIdentifier();
-
-            final long userToken = proto.start(FaceServiceDumpProto.USERS);
-
-            proto.write(FaceUserStatsProto.USER_ID, userId);
-            proto.write(FaceUserStatsProto.NUM_FACES,
-                    getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
-
-            // Normal face authentications (e.g. lockscreen)
-            final PerformanceStats normal = mPerformanceMap.get(userId);
-            if (normal != null) {
-                final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
-                proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
-                proto.write(FaceActionStatsProto.REJECT, normal.reject);
-                proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
-                proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
-                proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
-                proto.end(countsToken);
-            }
-
-            // Statistics about secure face transactions (e.g. to unlock password
-            // storage, make secure purchases, etc.)
-            final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
-            if (crypto != null) {
-                final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
-                proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
-                proto.write(FaceActionStatsProto.REJECT, crypto.reject);
-                proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
-                proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
-                proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
-                proto.end(countsToken);
-            }
-
-            proto.end(userToken);
-        }
-        proto.flush();
-        mPerformanceMap.clear();
-        mCryptoPerformanceMap.clear();
-    }
-
     private void dumpHal(FileDescriptor fd, String[] args) {
         // WARNING: CDD restricts image data from leaving TEE unencrypted on
         //          production devices:
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 864a793..5b04379 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -155,9 +155,9 @@
     // Whether a captive portal was found during the last network validation attempt.
     public boolean lastCaptivePortalDetected;
 
-    // Indicates the user was notified of a successful captive portal login since a portal was
-    // last detected.
-    public boolean captivePortalLoginNotified;
+    // Indicates the captive portal app was opened to show a login UI to the user, but the network
+    // has not validated yet.
+    public volatile boolean captivePortalValidationPending;
 
     // Set to true when partial connectivity was detected.
     public boolean partialConnectivity;
@@ -629,7 +629,7 @@
                 + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
                 + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
                 + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
-                + "captivePortalLoginNotified{" + captivePortalLoginNotified + "} "
+                + "captivePortalValidationPending{" + captivePortalValidationPending + "} "
                 + "partialConnectivity{" + partialConnectivity + "} "
                 + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
                 + "clat{" + clatd + "} "
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 395319d..dd3876d 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -64,6 +64,7 @@
     private DisplayWhiteBalanceThrottler mThrottler;
 
     private final float mLowLightAmbientColorTemperature;
+    private final float mHighLightAmbientColorTemperature;
 
     private float mAmbientColorTemperature;
 
@@ -83,9 +84,17 @@
     // A piecewise linear relationship between ambient and display color temperatures.
     private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
 
+    // In very low or very high brightness conditions ambient EQ should to set to a default
+    // instead of using mAmbientToDisplayColorTemperatureSpline. However, setting ambient EQ
+    // based on thresholds can cause the display to rapidly change color temperature. To solve
+    // this, mLowLightAmbientBrightnessToBiasSpline and mHighLightAmbientBrightnessToBiasSpline
+    // are used to smoothly interpolate from ambient color temperature to the defaults.
     // A piecewise linear relationship between low light brightness and low light bias.
     private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
 
+    // A piecewise linear relationship between high light brightness and high light bias.
+    private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+
     /**
      * @param brightnessSensor
      *      The sensor used to detect changes in the ambient brightness.
@@ -100,12 +109,22 @@
      * @param throttler
      *      The throttler used to determine whether the new display color temperature should be
      *      updated or not.
-     * @param lowLightAmbientBrightnessThreshold
-     *      The ambient brightness threshold beneath which we fall back to a fixed ambient color
-     *      temperature.
+     * @param lowLightAmbientBrightnesses
+     *      The ambient brightness used to map the ambient brightnesses to the biases used to
+     *      interpolate to lowLightAmbientColorTemperature.
+     * @param lowLightAmbientBiases
+     *      The biases used to map the ambient brightnesses to the biases used to interpolate to
+     *      lowLightAmbientColorTemperature.
      * @param lowLightAmbientColorTemperature
-     *      The ambient color temperature to which we fall back when the ambient brightness drops
-     *      beneath a certain threshold.
+     *      The ambient color temperature to which we interpolate to based on the low light curve.
+     * @param highLightAmbientBrightnesses
+     *      The ambient brightness used to map the ambient brightnesses to the biases used to
+     *      interpolate to highLightAmbientColorTemperature.
+     * @param highLightAmbientBiases
+     *      The biases used to map the ambient brightnesses to the biases used to interpolate to
+     *      highLightAmbientColorTemperature.
+     * @param highLightAmbientColorTemperature
+     *      The ambient color temperature to which we interpolate to based on the high light curve.
      * @param ambientColorTemperatures
      *      The ambient color tempeartures used to map the ambient color temperature to the display
      *      color temperature (or null if no mapping is necessary).
@@ -128,6 +147,8 @@
             @NonNull DisplayWhiteBalanceThrottler throttler,
             float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases,
             float lowLightAmbientColorTemperature,
+            float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases,
+            float highLightAmbientColorTemperature,
             float[] ambientColorTemperatures, float[] displayColorTemperatures) {
         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
                 colorTemperatureFilter, throttler);
@@ -140,6 +161,7 @@
         mColorTemperatureFilter = colorTemperatureFilter;
         mThrottler = throttler;
         mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+        mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
         mAmbientColorTemperature = -1.0f;
         mPendingAmbientColorTemperature = -1.0f;
         mLastAmbientColorTemperature = -1.0f;
@@ -158,12 +180,40 @@
                     mLowLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
                     != 1.0f) {
                 Slog.d(TAG, "invalid low light ambient brightness to bias spline, "
-                        + "bias must begin at 0.0 and end at 1.0");
+                        + "bias must begin at 0.0 and end at 1.0.");
                 mLowLightAmbientBrightnessToBiasSpline = null;
             }
         }
 
         try {
+            mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
+                    highLightAmbientBrightnesses, highLightAmbientBiases);
+        } catch (Exception e) {
+            Slog.e(TAG, "failed to create high light ambient brightness to bias spline.", e);
+            mHighLightAmbientBrightnessToBiasSpline = null;
+        }
+        if (mHighLightAmbientBrightnessToBiasSpline != null) {
+            if (mHighLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
+                    mHighLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
+                    != 1.0f) {
+                Slog.d(TAG, "invalid high light ambient brightness to bias spline, "
+                        + "bias must begin at 0.0 and end at 1.0.");
+                mHighLightAmbientBrightnessToBiasSpline = null;
+            }
+        }
+
+        if (mLowLightAmbientBrightnessToBiasSpline != null &&
+                mHighLightAmbientBrightnessToBiasSpline != null) {
+            if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
+                    highLightAmbientBrightnesses[0]) {
+                Slog.d(TAG, "invalid low light and high light ambient brightness to bias spline "
+                        + "combination, defined domains must not intersect.");
+                mLowLightAmbientBrightnessToBiasSpline = null;
+                mHighLightAmbientBrightnessToBiasSpline = null;
+            }
+        }
+
+        try {
             mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
                     ambientColorTemperatures, displayColorTemperatures);
         } catch (Exception e) {
@@ -264,6 +314,7 @@
         mColorTemperatureFilter.dump(writer);
         mThrottler.dump(writer);
         writer.println("  mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+        writer.println("  mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
         writer.println("  mAmbientColorTemperature=" + mAmbientColorTemperature);
         writer.println("  mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
@@ -273,6 +324,8 @@
                 + mAmbientToDisplayColorTemperatureSpline);
         writer.println("  mLowLightAmbientBrightnessToBiasSpline="
                 + mLowLightAmbientBrightnessToBiasSpline);
+        writer.println("  mHighLightAmbientBrightnessToBiasSpline="
+                + mHighLightAmbientBrightnessToBiasSpline);
     }
 
     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -309,6 +362,12 @@
                     bias * ambientColorTemperature + (1.0f - bias)
                     * mLowLightAmbientColorTemperature;
         }
+        if (mHighLightAmbientBrightnessToBiasSpline != null) {
+            float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+            ambientColorTemperature =
+                    (1.0f - bias) * ambientColorTemperature + bias
+                    * mHighLightAmbientColorTemperature;
+        }
 
         if (mAmbientColorTemperatureOverride != -1.0f) {
             if (mLoggingEnabled) {
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index b1b465e..4df7d6b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -72,6 +72,15 @@
         final float lowLightAmbientColorTemperature = getFloat(resources,
                 com.android.internal.R.dimen
                 .config_displayWhiteBalanceLowLightAmbientColorTemperature);
+        final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources,
+                com.android.internal.R.array
+                .config_displayWhiteBalanceHighLightAmbientBrightnesses);
+        final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources,
+                com.android.internal.R.array
+                .config_displayWhiteBalanceHighLightAmbientBiases);
+        final float highLightAmbientColorTemperature = getFloat(resources,
+                com.android.internal.R.dimen
+                .config_displayWhiteBalanceHighLightAmbientColorTemperature);
         final float[] ambientColorTemperatures = getFloatArray(resources,
                 com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
         final float[] displayColorTempeartures = getFloatArray(resources,
@@ -80,6 +89,8 @@
                 brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
                 throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
                 displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
+                displayWhiteBalanceHighLightAmbientBrightnesses,
+                displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
                 ambientColorTemperatures, displayColorTempeartures);
         brightnessSensor.setCallbacks(controller);
         colorTemperatureSensor.setCallbacks(controller);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 3a78aa2..037293f 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -50,6 +50,7 @@
 import android.provider.Telephony;
 import android.telecom.TelecomManager;
 import android.util.ArraySet;
+import android.util.LongSparseLongArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -58,6 +59,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.IntPair;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -147,11 +149,9 @@
                 PermissionInfo perm = dangerousPerms.get(i);
 
                 if (perm.isHardRestricted() || perm.backgroundPermission != null) {
-                    appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name),
-                            null, appOpsListener);
+                    appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
                 } else if (perm.isSoftRestricted()) {
-                    appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name),
-                            null, appOpsListener);
+                    appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
 
                     SoftRestrictedPermissionPolicy policy =
                             SoftRestrictedPermissionPolicy.forPermission(null, null, null,
@@ -167,6 +167,25 @@
         }
     }
 
+    /**
+     * Get op that controls the access related to the permission.
+     *
+     * <p>Usually the permission-op relationship is 1:1 but some permissions (e.g. fine location)
+     * {@link AppOpsManager#sOpToSwitch share an op} to control the access.
+     *
+     * @param permission The permission
+     *
+     * @return The op that controls the access of the permission
+     */
+    private static int getSwitchOp(@NonNull String permission) {
+        int op = AppOpsManager.permissionToOpCode(permission);
+        if (op == OP_NONE) {
+            return OP_NONE;
+        }
+
+        return AppOpsManager.opToSwitch(op);
+    }
+
     private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
             @UserIdInt int changedUserId) {
         if (isStarted(changedUserId)) {
@@ -430,40 +449,89 @@
          * <p>This processes ops previously added by {@link #addOpIfRestricted}
          */
         private void syncPackages() {
+            // Remember which ops were already set. This makes sure that we always set the most
+            // permissive mode if two OpChanges are scheduled. This can e.g. happen if two
+            // permissions change the same op. See {@link #getSwitchOp}.
+            LongSparseLongArray alreadySetAppOps = new LongSparseLongArray();
+
             final int allowCount = mOpsToAllow.size();
             for (int i = 0; i < allowCount; i++) {
                 final OpToChange op = mOpsToAllow.get(i);
+
                 setUidModeAllowed(op.code, op.uid, op.packageName);
+                alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
+
             final int allowIfDefaultCount = mOpsToAllowIfDefault.size();
             for (int i = 0; i < allowIfDefaultCount; i++) {
                 final OpToChange op = mOpsToAllowIfDefault.get(i);
-                setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
+                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+                    continue;
+                }
+
+                boolean wasSet = setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
+                if (wasSet) {
+                    alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
+                }
             }
-            final int foregroundCount = mOpsToForegroundIfAllow.size();
-            for (int i = 0; i < foregroundCount; i++) {
+
+            final int foregroundIfAllowedCount = mOpsToForegroundIfAllow.size();
+            for (int i = 0; i < foregroundIfAllowedCount; i++) {
                 final OpToChange op = mOpsToForegroundIfAllow.get(i);
-                setUidModeForegroundIfAllow(op.code, op.uid, op.packageName);
+                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+                    continue;
+                }
+
+                boolean wasSet = setUidModeForegroundIfAllow(op.code, op.uid, op.packageName);
+                if (wasSet) {
+                    alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
+                }
             }
-            final int foregroundIfAllowCount = mOpsToForeground.size();
-            for (int i = 0; i < foregroundIfAllowCount; i++) {
+
+            final int foregroundCount = mOpsToForeground.size();
+            for (int i = 0; i < foregroundCount; i++) {
                 final OpToChange op = mOpsToForeground.get(i);
+                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+                    continue;
+                }
+
                 setUidModeForeground(op.code, op.uid, op.packageName);
+                alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
+
             final int ignoreCount = mOpsToIgnore.size();
             for (int i = 0; i < ignoreCount; i++) {
                 final OpToChange op = mOpsToIgnore.get(i);
+                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+                    continue;
+                }
+
                 setUidModeIgnored(op.code, op.uid, op.packageName);
+                alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
+
             final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size();
             for (int i = 0; i < ignoreIfDefaultCount; i++) {
                 final OpToChange op = mOpsToIgnoreIfDefault.get(i);
-                setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+                    continue;
+                }
+
+                boolean wasSet = setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+                if (wasSet) {
+                    alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
+                }
             }
+
             final int defaultCount = mOpsToDefault.size();
             for (int i = 0; i < defaultCount; i++) {
                 final OpToChange op = mOpsToDefault.get(i);
+                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
+                    continue;
+                }
+
                 setUidModeDefault(op.code, op.uid, op.packageName);
+                alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
         }
 
@@ -479,7 +547,7 @@
         private void addOpIfRestricted(@NonNull PermissionInfo permissionInfo,
                 @NonNull PackageInfo pkg) {
             final String permission = permissionInfo.name;
-            final int opCode = AppOpsManager.permissionToOpCode(permission);
+            final int opCode = getSwitchOp(permission);
             final int uid = pkg.applicationInfo.uid;
 
             if (!permissionInfo.isRestricted()) {
@@ -581,7 +649,7 @@
             }
 
             final String permission = permissionInfo.name;
-            final int opCode = AppOpsManager.permissionToOpCode(permission);
+            final int opCode = getSwitchOp(permission);
             final String pkgName = pkg.packageName;
             final int uid = pkg.applicationInfo.uid;
 
@@ -641,7 +709,7 @@
             }
 
             for (String permission : pkg.requestedPermissions) {
-                final int opCode = AppOpsManager.permissionToOpCode(permission);
+                final int opCode = getSwitchOp(permission);
                 if (opCode == OP_NONE) {
                     continue;
                 }
@@ -658,24 +726,27 @@
             }
         }
 
-        private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) {
-            setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
+        private boolean setUidModeAllowedIfDefault(int opCode, int uid,
+                @NonNull String packageName) {
+            return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
         }
 
         private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_ALLOWED, packageName);
         }
 
-        private void setUidModeForegroundIfAllow(int opCode, int uid, @NonNull String packageName) {
-            setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
+        private boolean setUidModeForegroundIfAllow(int opCode, int uid,
+                @NonNull String packageName) {
+            return setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
         }
 
         private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
         }
 
-        private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) {
-            setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
+        private boolean setUidModeIgnoredIfDefault(int opCode, int uid,
+                @NonNull String packageName) {
+            return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
         }
 
         private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
@@ -692,14 +763,17 @@
             }
         }
 
-        private void setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
+        private boolean setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
                 @NonNull String packageName) {
             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
                     .opToPublicName(opCode), uid, packageName);
 
             if (currentMode == requiredModeBefore) {
                 mAppOpsManager.setUidMode(opCode, uid, newMode);
+                return true;
             }
+
+            return false;
         }
 
         private void setUidModeDefault(int opCode, int uid, String packageName) {
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 1658833..d53f685 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -91,8 +91,7 @@
             // Storage uses a special app op to decide the mount state and supports soft restriction
             // where the restricted state allows the permission but only for accessing the medial
             // collections.
-            case READ_EXTERNAL_STORAGE:
-            case WRITE_EXTERNAL_STORAGE: {
+            case READ_EXTERNAL_STORAGE: {
                 final int flags;
                 final boolean applyRestriction;
                 final boolean isWhiteListed;
@@ -148,6 +147,42 @@
                     }
                 };
             }
+            case WRITE_EXTERNAL_STORAGE: {
+                final boolean isWhiteListed;
+                final int targetSDK;
+
+                if (appInfo != null) {
+                    final int flags = context.getPackageManager().getPermissionFlags(permission,
+                            appInfo.packageName, user);
+                    isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+                    targetSDK = appInfo.targetSdkVersion;
+                } else {
+                    isWhiteListed = false;
+                    targetSDK = 0;
+                }
+
+                return new SoftRestrictedPermissionPolicy() {
+                    @Override
+                    public int resolveAppOp() {
+                        return OP_NONE;
+                    }
+
+                    @Override
+                    public int getDesiredOpMode() {
+                        return MODE_DEFAULT;
+                    }
+
+                    @Override
+                    public boolean shouldSetAppOpIfNotDefault() {
+                        return false;
+                    }
+
+                    @Override
+                    public boolean canBeGranted() {
+                        return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
+                    }
+                };
+            }
             default:
                 return DUMMY_POLICY;
         }
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index c7f8c07..c7e241b 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -41,6 +41,9 @@
       "options": [
         {
           "include-filter": "android.permission.cts.SplitPermissionTest"
+        },
+        {
+          "include-filter": "android.permission.cts.BackgroundPermissionsTest"
         }
       ]
     }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d6a9f427..7408dd4 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -199,6 +199,7 @@
         private final Uri LOCK_SCREEN_WHEN_TRUST_LOST =
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST);
 
+        private final boolean mIsAutomotive;
         private final ContentResolver mContentResolver;
         private boolean mTrustAgentsExtendUnlock;
         private boolean mLockWhenTrustLost;
@@ -210,6 +211,10 @@
          */
         SettingsObserver(Handler handler) {
             super(handler);
+
+            PackageManager packageManager = getContext().getPackageManager();
+            mIsAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+
             mContentResolver = getContext().getContentResolver();
             updateContentObserver();
         }
@@ -233,11 +238,15 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
+                // Smart lock should only extend unlock. The only exception is for automotive,
+                // where it can actively unlock the head unit.
+                int defaultValue = mIsAutomotive ? 0 : 1;
+
                 mTrustAgentsExtendUnlock =
                         Settings.Secure.getIntForUser(
                                 mContentResolver,
                                 Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
-                                1 /* default */,
+                                defaultValue,
                                 mCurrentUser) != 0;
             } else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) {
                 mLockWhenTrustLost =
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7badc7a..cf87203 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -478,8 +478,10 @@
 
                     @Override
                     public void onSwipeFromRight() {
-                        final Region excludedRegion =
-                                mDisplayContent.calculateSystemGestureExclusion();
+                        final Region excludedRegion;
+                        synchronized (mLock) {
+                            excludedRegion = mDisplayContent.calculateSystemGestureExclusion();
+                        }
                         final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
                                 || mNavigationBarPosition == NAV_BAR_RIGHT;
                         if (mNavigationBar != null && sideAllowed
@@ -490,8 +492,10 @@
 
                     @Override
                     public void onSwipeFromLeft() {
-                        final Region excludedRegion =
-                                mDisplayContent.calculateSystemGestureExclusion();
+                        final Region excludedRegion;
+                        synchronized (mLock) {
+                            excludedRegion = mDisplayContent.calculateSystemGestureExclusion();
+                        }
                         final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
                                 || mNavigationBarPosition == NAV_BAR_LEFT;
                         if (mNavigationBar != null && sideAllowed
@@ -1494,8 +1498,6 @@
         }
 
         sTmpRect.setEmpty();
-        sTmpDockedFrame.set(displayFrames.mDock);
-
         final int displayId = displayFrames.mDisplayId;
         final Rect dockFrame = displayFrames.mDock;
         final int displayHeight = displayFrames.mDisplayHeight;
@@ -1508,11 +1510,13 @@
                 continue;
             }
 
-            w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
-                    sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
-                    sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
-                    sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
-                    sTmpDockedFrame /* outsetFrame */);
+            w.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
+                    displayFrames.mUnrestricted /* displayFrame */,
+                    displayFrames.mUnrestricted /* overscanFrame */,
+                    displayFrames.mUnrestricted /* contentFrame */,
+                    displayFrames.mUnrestricted /* visibleFrame */, sTmpRect /* decorFrame */,
+                    displayFrames.mUnrestricted /* stableFrame */,
+                    displayFrames.mUnrestricted /* outsetFrame */);
             w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
             w.computeFrameLw();
             final Rect frame = w.getFrameLw();
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index ba23258..af72931 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -25,6 +25,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.NonNull;
 import android.app.RemoteAction;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
@@ -45,6 +46,7 @@
 import android.view.IPinnedStackListener;
 
 import com.android.internal.policy.PipSnapAlgorithm;
+import com.android.internal.util.Preconditions;
 import com.android.server.UiThread;
 
 import java.io.PrintWriter;
@@ -326,8 +328,8 @@
     boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
         synchronized (mService.mGlobalLock) {
             final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
-            if (mDisplayInfo.equals(displayInfo)) {
-                // We are already in the right orientation, ignore
+            if (isSameDimensionAndRotation(mDisplayInfo, displayInfo)) {
+                // No dimension/rotation change, ignore
                 outBounds.setEmpty();
                 return false;
             } else if (targetBounds.isEmpty()) {
@@ -427,6 +429,15 @@
         notifyActionsChanged(mActions);
     }
 
+    private boolean isSameDimensionAndRotation(@NonNull DisplayInfo display1,
+            @NonNull DisplayInfo display2) {
+        Preconditions.checkNotNull(display1);
+        Preconditions.checkNotNull(display2);
+        return ((display1.rotation == display2.rotation)
+                && (display1.logicalWidth == display2.logicalWidth)
+                && (display1.logicalHeight == display2.logicalHeight));
+    }
+
     /**
      * Notifies listeners that the PIP needs to be adjusted for the IME.
      */
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3820106..fd86faa 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -348,6 +348,9 @@
     void onDisplayChanged(DisplayContent dc) {
         adjustBoundsForDisplayChangeIfNeeded(dc);
         super.onDisplayChanged(dc);
+        final int displayId = (dc != null) ? dc.getDisplayId() : Display.INVALID_DISPLAY;
+        mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
+                mTaskId, displayId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 27175c7..f776062 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -55,6 +55,7 @@
     private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
     private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
     private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
+    private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 23;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -159,6 +160,10 @@
         l.onSingleTaskDisplayDrawn(m.arg1);
     };
 
+    private final TaskStackConsumer mNotifyTaskDisplayChanged = (l, m) -> {
+        l.onTaskDisplayChanged(m.arg1, m.arg2);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -241,6 +246,9 @@
                 case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
                     forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
                     break;
+                case NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskDisplayChanged, msg);
+                    break;
             }
         }
     }
@@ -495,4 +503,14 @@
         forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify listeners that a task is reparented to another display.
+     */
+    void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG,
+                taskId, newDisplayId);
+        forAllLocalListeners(mNotifyTaskStackChanged, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 481c3ba..114a56f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1936,8 +1936,12 @@
     public boolean setPinnedStackAlpha(float alpha) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
         synchronized (mWmService.mGlobalLock) {
-            getPendingTransaction().setAlpha(getSurfaceControl(),
-                    mCancelCurrentBoundsAnimation ? 1 : alpha);
+            final SurfaceControl sc = getSurfaceControl();
+            if (sc == null || !sc.isValid()) {
+                // If the stack is already removed, don't bother updating any stack animation
+                return false;
+            }
+            getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
             scheduleAnimation();
             return !mCancelCurrentBoundsAnimation;
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3cccd26..c75e0da 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7693,7 +7693,7 @@
 
     private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
         final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
-        if (touchedWindow == null) {
+        if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
             return;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index de68036..424142c 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -23,7 +23,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import org.mockito.stubbing.Answer;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -56,7 +61,8 @@
 public final class AmbientLuxTest {
     private static final int AMBIENT_COLOR_TYPE = 20705;
     private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
-    private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5700;
+    private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
+    private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f;
 
     private Handler mHandler = new Handler(Looper.getMainLooper());
     private Sensor mLightSensor;
@@ -68,6 +74,8 @@
 
     @Mock private TypedArray mBrightnesses;
     @Mock private TypedArray mBiases;
+    @Mock private TypedArray mHighLightBrightnesses;
+    @Mock private TypedArray mHighLightBiases;
 
     @Before
     public void setUp() throws Exception {
@@ -89,9 +97,10 @@
         when(mResourcesSpy.getInteger(
                 R.integer.config_displayWhiteBalanceIncreaseDebounce))
                 .thenReturn(0);
-        when(mResourcesSpy.getFloat(
-                R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature))
-                .thenReturn(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+        mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature,
+                LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+        mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature,
+                HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
         when(mResourcesSpy.obtainTypedArray(
                 R.array.config_displayWhiteBalanceAmbientColorTemperatures))
                 .thenReturn(createTypedArray());
@@ -105,6 +114,13 @@
         when(mResourcesSpy.obtainTypedArray(
                 R.array.config_displayWhiteBalanceLowLightAmbientBiases))
                 .thenReturn(mBiases);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceHighLightAmbientBrightnesses))
+                .thenReturn(mHighLightBrightnesses);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceHighLightAmbientBiases))
+                .thenReturn(mHighLightBiases);
+        mockThrottler();
     }
 
     @Test
@@ -216,16 +232,119 @@
     }
 
     @Test
-    public void testSpline_InvalidBiases() throws Exception {
-        float[][] invalidBrightnesses =
-                {{10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f},
-                 {10.0f, 1000.0f}, {-1.0f, 1.0f}, {-1.0f, 1.0f}};
-        float[][] invalidBiases =
-                {{0.0f, 2.0f}, {0.0f, 0.9f}, {0.1f, 1.0f}, {-0.1f, 1.0f},
-                 {0.1f, 1.1f}, {0.0f, 1.0f}, {-2.0f, 1.0f}};
-        for (int i = 0; i < invalidBrightnesses.length; ++i) {
-            setBrightnesses(invalidBrightnesses[i][0], invalidBrightnesses[i][1]);
-            setBiases(invalidBiases[i][0], invalidBiases[i][1]);
+    public void testSpline_InvalidEndBias() throws Exception {
+        setBrightnesses(10.0f, 1000.0f);
+        setBiases(0.0f, 2.0f);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 8000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+        setEstimatedBrightnessAndUpdate(controller, luxOverride);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                ambientColorTemperature, 0.001);
+        }
+    }
+
+    @Test
+    public void testSpline_InvalidBeginBias() throws Exception {
+        setBrightnesses(10.0f, 1000.0f);
+        setBiases(0.1f, 1.0f);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 8000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+        setEstimatedBrightnessAndUpdate(controller, luxOverride);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                ambientColorTemperature, 0.001);
+        }
+    }
+
+    @Test
+    public void testSpline_OneSegmentHighLight() throws Exception {
+        final float lowerBrightness = 10.0f;
+        final float upperBrightness = 50.0f;
+        setHighLightBrightnesses(lowerBrightness, upperBrightness);
+        setHighLightBiases(0.0f, 1.0f);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 8000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+            setEstimatedBrightnessAndUpdate(controller,
+                    mix(lowerBrightness, upperBrightness, t));
+            assertEquals(controller.mPendingAmbientColorTemperature,
+                    mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - t),
+                    0.001);
+        }
+
+        setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
+
+        setEstimatedBrightnessAndUpdate(controller, 0.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
+    }
+
+    @Test
+    public void testSpline_TwoSegmentsHighLight() throws Exception {
+        final float brightness0 = 10.0f;
+        final float brightness1 = 50.0f;
+        final float brightness2 = 60.0f;
+        setHighLightBrightnesses(brightness0, brightness1, brightness2);
+        final float bias0 = 0.0f;
+        final float bias1 = 0.25f;
+        final float bias2 = 1.0f;
+        setHighLightBiases(bias0, bias1, bias2);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 6000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+            float luxOverride = mix(brightness0, brightness1, t);
+            setEstimatedBrightnessAndUpdate(controller, luxOverride);
+            float bias = mix(bias0, bias1, t);
+            assertEquals(controller.mPendingAmbientColorTemperature,
+                    mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
+                    0.01);
+        }
+
+        for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+            float luxOverride = mix(brightness1, brightness2, t);
+            setEstimatedBrightnessAndUpdate(controller, luxOverride);
+            float bias = mix(bias1, bias2, t);
+            assertEquals(controller.mPendingAmbientColorTemperature,
+                    mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
+                    0.01);
+        }
+
+        setEstimatedBrightnessAndUpdate(controller, brightness2 + 1.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
+
+        setEstimatedBrightnessAndUpdate(controller, 0.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
+    }
+
+    @Test
+    public void testSpline_InvalidCombinations() throws Exception {
+            setBrightnesses(100.0f, 200.0f);
+            setBiases(0.0f, 1.0f);
+            setHighLightBrightnesses(150.0f, 250.0f);
+            setHighLightBiases(0.0f, 1.0f);
 
             DisplayWhiteBalanceController controller =
                     DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
@@ -238,7 +357,44 @@
                 assertEquals(controller.mPendingAmbientColorTemperature,
                         ambientColorTemperature, 0.001);
             }
-        }
+    }
+
+    void mockThrottler() {
+        when(mResourcesSpy.getInteger(
+                R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
+        when(mResourcesSpy.getInteger(
+                R.integer.config_displayWhiteBalanceIncreaseDebounce)).thenReturn(0);
+        TypedArray base = mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceBaseThresholds);
+        TypedArray inc = mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceIncreaseThresholds);
+        TypedArray dec = mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceDecreaseThresholds);
+        base = spy(base);
+        inc = spy(inc);
+        dec = spy(dec);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceBaseThresholds)).thenReturn(base);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceIncreaseThresholds)).thenReturn(inc);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceDecreaseThresholds)).thenReturn(dec);
+        setFloatArrayResource(base, new float[]{0.0f});
+        setFloatArrayResource(inc, new float[]{0.0f});
+        setFloatArrayResource(dec, new float[]{0.0f});
+    }
+
+    private void mockResourcesFloat(int id, float floatValue) {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                TypedValue value = (TypedValue)invocation.getArgument(1);
+                value.type = TypedValue.TYPE_FLOAT;
+                value.data = Float.floatToIntBits(floatValue);
+                return null;
+            }
+        }).when(mResourcesSpy).getValue(
+                eq(id),
+                any(TypedValue.class), eq(true));
     }
 
     private void setEstimatedColorTemperature(DisplayWhiteBalanceController controller,
@@ -262,6 +418,14 @@
         setFloatArrayResource(mBiases, vals);
     }
 
+    private void setHighLightBrightnesses(float... vals) {
+        setFloatArrayResource(mHighLightBrightnesses, vals);
+    }
+
+    private void setHighLightBiases(float... vals) {
+        setFloatArrayResource(mHighLightBiases, vals);
+    }
+
     private void setFloatArrayResource(TypedArray array, float[] vals) {
         when(array.length()).thenReturn(vals.length);
         for (int i = 0; i < vals.length; i++) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 4a87aa4..de184b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -16,6 +16,10 @@
 
 package com.android.server.wm;
 
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.RIGHT;
+import static android.view.Gravity.TOP;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
@@ -26,9 +30,11 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -66,6 +72,7 @@
     private WindowState mWindow;
     private int mRotation = ROTATION_0;
     private boolean mHasDisplayCutout;
+    private static final int DECOR_WINDOW_INSET = 50;
 
     @Before
     public void setUp() throws Exception {
@@ -520,6 +527,58 @@
         }
     }
 
+    @Test
+    public void testScreenDecorWindows() {
+        synchronized (mWm.mGlobalLock) {
+            final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY,
+                    "decorWindow");
+            decorWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+            decorWindow.mAttrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+            addWindow(decorWindow);
+            addWindow(mWindow);
+
+            // Decor on top
+            updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP);
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT);
+
+            // Decor on bottom
+            updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, BOTTOM);
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT,
+                    DECOR_WINDOW_INSET);
+
+            // Decor on the left
+            updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, LEFT);
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+            assertInsetBy(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0,
+                    NAV_BAR_HEIGHT);
+
+            // Decor on the right
+            updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, RIGHT);
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+            assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET,
+                    NAV_BAR_HEIGHT);
+
+            // Decor not allowed as inset
+            updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, DECOR_WINDOW_INSET, TOP);
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        }
+    }
+
+    private void updateDecorWindow(WindowState decorWindow, int width, int height, int gravity) {
+        decorWindow.mAttrs.width = width;
+        decorWindow.mAttrs.height = height;
+        decorWindow.mAttrs.gravity = gravity;
+        decorWindow.setRequestedSize(width, height);
+    }
+
     /**
      * Asserts that {@code actual} is inset by the given amounts from the full display rect.
      *
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 366acea..d0ee634 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -162,6 +162,9 @@
         }
 
         final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+        final TaskChangeNotificationController taskChangeNotificationController = mock(
+                TaskChangeNotificationController.class);
+        doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController();
         final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
         doReturn(wmLock).when(atms).getGlobalLock();
 
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index 2f1505c..5e4818a 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -19,6 +19,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -69,18 +70,28 @@
     private long mConnectElapsedTime;
 
     /**
+     * The direction of the call;
+     * {@link Call.Details#DIRECTION_INCOMING} for incoming calls, or
+     * {@link Call.Details#DIRECTION_OUTGOING} for outgoing calls.
+     */
+    private int mCallDirection;
+
+    /**
      * Creates an instance of {@code ConferenceParticipant}.
      *
      * @param handle      The conference participant's handle (e.g., phone number).
      * @param displayName The display name for the participant.
      * @param endpoint    The enpoint Uri which uniquely identifies this conference participant.
      * @param state       The state of the participant in the conference.
+     * @param callDirection The direction of the call (incoming/outgoing).
      */
-    public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state) {
+    public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state,
+            int callDirection) {
         mHandle = handle;
         mDisplayName = displayName;
         mEndpoint = endpoint;
         mState = state;
+        mCallDirection = callDirection;
     }
 
     /**
@@ -96,7 +107,16 @@
                     String displayName = source.readString();
                     Uri endpoint = source.readParcelable(classLoader);
                     int state = source.readInt();
-                    return new ConferenceParticipant(handle, displayName, endpoint, state);
+                    long connectTime = source.readLong();
+                    long elapsedRealTime = source.readLong();
+                    int callDirection = source.readInt();
+                    ConferenceParticipant participant =
+                            new ConferenceParticipant(handle, displayName, endpoint, state,
+                                    callDirection);
+                    participant.setConnectTime(connectTime);
+                    participant.setConnectElapsedTime(elapsedRealTime);
+                    participant.setCallDirection(callDirection);
+                    return participant;
                 }
 
                 @Override
@@ -170,6 +190,9 @@
         dest.writeString(mDisplayName);
         dest.writeParcelable(mEndpoint, 0);
         dest.writeInt(mState);
+        dest.writeLong(mConnectTime);
+        dest.writeLong(mConnectElapsedTime);
+        dest.writeInt(mCallDirection);
     }
 
     /**
@@ -192,6 +215,8 @@
         sb.append(getConnectTime());
         sb.append(" ConnectElapsedTime: ");
         sb.append(getConnectElapsedTime());
+        sb.append(" Direction: ");
+        sb.append(getCallDirection() == Call.Details.DIRECTION_INCOMING ? "Incoming" : "Outgoing");
         sb.append("]");
         return sb.toString();
     }
@@ -239,7 +264,7 @@
     }
 
     /**
-     * The connect elpased time of the participant to the conference.
+     * The connect elapsed time of the participant to the conference.
      */
     public long getConnectElapsedTime() {
         return mConnectElapsedTime;
@@ -248,4 +273,76 @@
     public void setConnectElapsedTime(long connectElapsedTime) {
         mConnectElapsedTime = connectElapsedTime;
     }
+
+    /**
+     * @return The direction of the call (incoming/outgoing).
+     */
+    public @Call.Details.CallDirection int getCallDirection() {
+        return mCallDirection;
+    }
+
+    /**
+     * Sets the direction of the call.
+     * @param callDirection Whether the call is incoming or outgoing.
+     */
+    public void setCallDirection(@Call.Details.CallDirection int callDirection) {
+        mCallDirection = callDirection;
+    }
+
+    /**
+     * Attempts to build a tel: style URI from a conference participant.
+     * Conference event package data contains SIP URIs, so we try to extract the phone number and
+     * format into a typical tel: style URI.
+     *
+     * @param address The conference participant's address.
+     * @param countryIso The country ISO of the current subscription; used when formatting the
+     *                   participant phone number to E.164 format.
+     * @return The participant's address URI.
+     * @hide
+     */
+    @VisibleForTesting
+    public static Uri getParticipantAddress(Uri address, String countryIso) {
+        if (address == null) {
+            return address;
+        }
+        // Even if address is already in tel: format, still parse it and rebuild.
+        // This is to recognize tel URIs such as:
+        // tel:6505551212;phone-context=ims.mnc012.mcc034.3gppnetwork.org
+
+        // Conference event package participants are identified using SIP URIs (see RFC3261).
+        // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+        // Per RFC3261, the "user" can be a telephone number.
+        // For example: sip:1650555121;phone-context=blah.com@host.com
+        // In this case, the phone number is in the user field of the URI, and the parameters can be
+        // ignored.
+        //
+        // A SIP URI can also specify a phone number in a format similar to:
+        // sip:+1-212-555-1212@something.com;user=phone
+        // In this case, the phone number is again in user field and the parameters can be ignored.
+        // We can get the user field in these instances by splitting the string on the @, ;, or :
+        // and looking at the first found item.
+        String number = address.getSchemeSpecificPart();
+        if (TextUtils.isEmpty(number)) {
+            return address;
+        }
+
+        String numberParts[] = number.split("[@;:]");
+        if (numberParts.length == 0) {
+            return address;
+        }
+        number = numberParts[0];
+
+        // Attempt to format the number in E.164 format and use that as part of the TEL URI.
+        // RFC2806 recommends to format telephone numbers using E.164 since it is independent of
+        // how the dialing of said numbers takes place.
+        // If conversion to E.164 fails, the returned value is null.  In that case, fallback to the
+        // number which was in the CEP data.
+        String formattedNumber = null;
+        if (!TextUtils.isEmpty(countryIso)) {
+            formattedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+        }
+
+        return Uri.fromParts(PhoneAccount.SCHEME_TEL,
+                formattedNumber != null ? formattedNumber : number, null);
+    }
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 47587c5..0983eea 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1793,6 +1793,11 @@
     private ConnectionService mConnectionService;
     private Bundle mExtras;
     private final Object mExtrasLock = new Object();
+    /**
+     * The direction of the connection; used where an existing connection is created and we need to
+     * communicate to Telecom whether its incoming or outgoing.
+     */
+    private @Call.Details.CallDirection int mCallDirection = Call.Details.DIRECTION_UNKNOWN;
 
     /**
      * Tracks the key set for the extras bundle provided on the last invocation of
@@ -3357,4 +3362,21 @@
             l.onConnectionEvent(this, event, extras);
         }
     }
+
+    /**
+     * @return The direction of the call.
+     * @hide
+     */
+    public final @Call.Details.CallDirection int getCallDirection() {
+        return mCallDirection;
+    }
+
+    /**
+     * Sets the direction of this connection.
+     * @param callDirection The direction of this connection.
+     * @hide
+     */
+    public void setCallDirection(@Call.Details.CallDirection int callDirection) {
+        mCallDirection = callDirection;
+    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 626fcc4..3548810 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2144,7 +2144,8 @@
                     connection.getDisconnectCause(),
                     emptyList,
                     connection.getExtras(),
-                    conferenceId);
+                    conferenceId,
+                    connection.getCallDirection());
             mAdapter.addExistingConnection(id, parcelableConnection);
         }
     }
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index dab1c6e..4734af6 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -53,6 +53,7 @@
     private final List<String> mConferenceableConnectionIds;
     private final Bundle mExtras;
     private String mParentCallId;
+    private @Call.Details.CallDirection int mCallDirection;
 
     /** @hide */
     public ParcelableConnection(
@@ -75,13 +76,15 @@
             DisconnectCause disconnectCause,
             List<String> conferenceableConnectionIds,
             Bundle extras,
-            String parentCallId) {
+            String parentCallId,
+            @Call.Details.CallDirection int callDirection) {
         this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address,
                 addressPresentation, callerDisplayName, callerDisplayNamePresentation,
                 videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis,
                 connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds,
                 extras);
         mParentCallId = parentCallId;
+        mCallDirection = callDirection;
     }
 
     /** @hide */
@@ -125,6 +128,7 @@
         mConferenceableConnectionIds = conferenceableConnectionIds;
         mExtras = extras;
         mParentCallId = null;
+        mCallDirection = Call.Details.DIRECTION_UNKNOWN;
     }
 
     public PhoneAccountHandle getPhoneAccount() {
@@ -219,6 +223,10 @@
         return mParentCallId;
     }
 
+    public @Call.Details.CallDirection int getCallDirection() {
+        return mCallDirection;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder()
@@ -234,6 +242,8 @@
                 .append(mExtras)
                 .append(", parent:")
                 .append(mParentCallId)
+                .append(", callDirection:")
+                .append(mCallDirection)
                 .toString();
     }
 
@@ -265,6 +275,7 @@
             int supportedAudioRoutes = source.readInt();
             String parentCallId = source.readString();
             long connectElapsedTimeMillis = source.readLong();
+            int callDirection = source.readInt();
 
             return new ParcelableConnection(
                     phoneAccount,
@@ -286,7 +297,8 @@
                     disconnectCause,
                     conferenceableConnectionIds,
                     extras,
-                    parentCallId);
+                    parentCallId,
+                    callDirection);
         }
 
         @Override
@@ -325,5 +337,6 @@
         destination.writeInt(mSupportedAudioRoutes);
         destination.writeString(mParentCallId);
         destination.writeLong(mConnectElapsedTimeMillis);
+        destination.writeInt(mCallDirection);
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index f2f3c2d..dc026d4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -68,8 +68,7 @@
     void notifyOtaspChanged(in int subId, in int otaspMode);
     @UnsupportedAppUsage
     void notifyCellInfo(in List<CellInfo> cellInfo);
-    void notifyPhysicalChannelConfiguration(in List<PhysicalChannelConfig> configs);
-    void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
+    void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
             in List<PhysicalChannelConfig> configs);
     void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
             int foregroundCallState, int backgroundCallState);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index cd831e2..37cc304 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
@@ -80,13 +81,13 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -141,6 +142,7 @@
 import android.net.NetworkMisc;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
+import android.net.NetworkStack;
 import android.net.NetworkStackClient;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
@@ -154,6 +156,7 @@
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -191,6 +194,7 @@
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
@@ -283,6 +287,7 @@
     @Mock NetworkStackClient mNetworkStack;
     @Mock PackageManager mPackageManager;
     @Mock UserManager mUserManager;
+    @Mock NotificationManager mNotificationManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -352,7 +357,7 @@
         @Override
         public Object getSystemService(String name) {
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
-            if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class);
+            if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
             if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             return super.getSystemService(name);
@@ -372,7 +377,17 @@
         public PackageManager getPackageManager() {
             return mPackageManager;
         }
-   }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String permission, String message) {
+            // The mainline permission can only be held if signed with the network stack certificate
+            // Skip testing for this permission.
+            if (NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK.equals(permission)) return;
+            // All other permissions should be held by the test or unnecessary: check as normal to
+            // make sure the code does not rely on unexpected permissions.
+            super.enforceCallingOrSelfPermission(permission, message);
+        }
+    }
 
     public void waitForIdle(int timeoutMsAsInt) {
         long timeoutMs = timeoutMsAsInt;
@@ -2857,6 +2872,9 @@
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
         validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        // Expect no notification to be shown when captive portal disappears by itself
+        verify(mNotificationManager, never()).notifyAsUser(
+                anyString(), eq(NotificationType.LOGGED_IN.eventId), any(), any());
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2886,6 +2904,8 @@
         // Check that calling startCaptivePortalApp does nothing.
         final int fastTimeoutMs = 100;
         mCm.startCaptivePortalApp(wifiNetwork);
+        waitForIdle();
+        verify(mWiFiNetworkAgent.mNetworkMonitor, never()).launchCaptivePortalApp();
         mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
 
         // Turn into a captive portal.
@@ -2896,14 +2916,26 @@
 
         // Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
         mCm.startCaptivePortalApp(wifiNetwork);
-        verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1))
-                .launchCaptivePortalApp();
+        waitForIdle();
+        verify(mWiFiNetworkAgent.mNetworkMonitor).launchCaptivePortalApp();
+
+        // NetworkMonitor uses startCaptivePortal(Network, Bundle) (startCaptivePortalAppInternal)
+        final Bundle testBundle = new Bundle();
+        final String testKey = "testkey";
+        final String testValue = "testvalue";
+        testBundle.putString(testKey, testValue);
+        mCm.startCaptivePortalApp(wifiNetwork, testBundle);
+        final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS);
+        assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction());
+        assertEquals(testValue, signInIntent.getStringExtra(testKey));
 
         // Report that the captive portal is dismissed, and check that callbacks are fired
         mWiFiNetworkAgent.setNetworkValid();
         mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        verify(mNotificationManager, times(1)).notifyAsUser(anyString(),
+                eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL));
 
         mCm.unregisterNetworkCallback(validatedCallback);
         mCm.unregisterNetworkCallback(captivePortalCallback);
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index 388c7d0..c50229a 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -16,9 +16,7 @@
 
 package com.android.framework.permission.tests;
 
-import junit.framework.TestCase;
-
-import android.media.AudioManager;
+import android.media.AudioAttributes;
 import android.os.Binder;
 import android.os.IVibratorService;
 import android.os.Process;
@@ -27,6 +25,9 @@
 import android.os.VibrationEffect;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import junit.framework.TestCase;
+
+
 /**
  * Verify that Hardware apis cannot be called without required permissions.
  */
@@ -51,7 +52,10 @@
         try {
             final VibrationEffect effect =
                     VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
-            mVibratorService.vibrate(Process.myUid(), null, effect, AudioManager.STREAM_ALARM,
+            final AudioAttributes attrs = new AudioAttributes.Builder()
+                    .setUsage(AudioAttributes.USAGE_ALARM)
+                    .build();
+            mVibratorService.vibrate(Process.myUid(), null, effect, attrs,
                     "testVibrate", new Binder());
             fail("vibrate did not throw SecurityException as expected");
         } catch (SecurityException e) {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index daea601..0511f24 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -31,11 +31,8 @@
 import java.util.Objects;
 
 /**
- * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects
- * directly but obtain them using
- * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or
- * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase)
- * versions.
+ * Network specifier object used to request a Wi-Fi Aware network. Apps should use the
+ * {@link WifiAwareNetworkSpecifier.Builder} class to create an instance.
  */
 public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
     /**